1 /*
2 * HEIF codec.
3 * Copyright (c) 2017 struktur AG, Dirk Farin <farin@struktur.de>
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 #if defined(HAVE_CONFIG_H)
21 #include "config.h"
22 #endif
23
24 #include "box.h"
25 #include "heif_limits.h"
26 #include "nclx.h"
27
28 #include <iomanip>
29 #include <utility>
30 #include <iostream>
31 #include <algorithm>
32 #include <cstring>
33 #include <cassert>
34
35
36 using namespace heif;
37
38
39 heif::Error heif::Error::Ok(heif_error_Ok);
40
41
Fraction(int32_t num,int32_t den)42 Fraction::Fraction(int32_t num, int32_t den)
43 {
44 // Reduce resolution of fraction until we are in a safe range.
45 // We need this as adding fractions may lead to very large denominators
46 // (e.g. 0x10000 * 0x10000 > 0x100000000 -> overflow, leading to integer 0)
47
48 numerator = num;
49 denominator = den;
50
51 while (denominator > MAX_FRACTION_VALUE || denominator < -MAX_FRACTION_VALUE) {
52 numerator /= 2;
53 denominator /= 2;
54 }
55
56 while (numerator > MAX_FRACTION_VALUE || numerator < -MAX_FRACTION_VALUE) {
57 numerator /= 2;
58 denominator /= 2;
59 }
60 }
61
operator +(const Fraction & b) const62 Fraction Fraction::operator+(const Fraction& b) const
63 {
64 if (denominator == b.denominator) {
65 return Fraction{numerator + b.numerator, denominator};
66 }
67 else {
68 return Fraction{numerator * b.denominator + b.numerator * denominator,
69 denominator * b.denominator};
70 }
71 }
72
operator -(const Fraction & b) const73 Fraction Fraction::operator-(const Fraction& b) const
74 {
75 if (denominator == b.denominator) {
76 return Fraction{numerator - b.numerator, denominator};
77 }
78 else {
79 return Fraction{numerator * b.denominator - b.numerator * denominator,
80 denominator * b.denominator};
81 }
82 }
83
operator +(int v) const84 Fraction Fraction::operator+(int v) const
85 {
86 return Fraction{numerator + v * denominator, denominator};
87 }
88
operator -(int v) const89 Fraction Fraction::operator-(int v) const
90 {
91 return Fraction{numerator - v * denominator, denominator};
92 }
93
operator /(int v) const94 Fraction Fraction::operator/(int v) const
95 {
96 return Fraction{numerator, denominator * v};
97 }
98
round_down() const99 int Fraction::round_down() const
100 {
101 return numerator / denominator;
102 }
103
round_up() const104 int Fraction::round_up() const
105 {
106 return (numerator + denominator - 1) / denominator;
107 }
108
round() const109 int Fraction::round() const
110 {
111 return (numerator + denominator / 2) / denominator;
112 }
113
is_valid() const114 bool Fraction::is_valid() const
115 {
116 return denominator != 0;
117 }
118
from_fourcc(const char * string)119 uint32_t from_fourcc(const char* string)
120 {
121 return ((string[0] << 24) |
122 (string[1] << 16) |
123 (string[2] << 8) |
124 (string[3]));
125 }
126
to_fourcc(uint32_t code)127 static std::string to_fourcc(uint32_t code)
128 {
129 std::string str(" ");
130 str[0] = static_cast<char>((code >> 24) & 0xFF);
131 str[1] = static_cast<char>((code >> 16) & 0xFF);
132 str[2] = static_cast<char>((code >> 8) & 0xFF);
133 str[3] = static_cast<char>((code >> 0) & 0xFF);
134
135 return str;
136 }
137
138
BoxHeader()139 heif::BoxHeader::BoxHeader()
140 {
141 }
142
143
get_type() const144 std::vector<uint8_t> heif::BoxHeader::get_type() const
145 {
146 if (m_type == fourcc("uuid")) {
147 return m_uuid_type;
148 }
149 else {
150 std::vector<uint8_t> type(4);
151 type[0] = static_cast<uint8_t>((m_type >> 24) & 0xFF);
152 type[1] = static_cast<uint8_t>((m_type >> 16) & 0xFF);
153 type[2] = static_cast<uint8_t>((m_type >> 8) & 0xFF);
154 type[3] = static_cast<uint8_t>((m_type >> 0) & 0xFF);
155 return type;
156 }
157 }
158
159
get_type_string() const160 std::string heif::BoxHeader::get_type_string() const
161 {
162 if (m_type == fourcc("uuid")) {
163 // 8-4-4-4-12
164
165 std::ostringstream sstr;
166 sstr << std::hex;
167 sstr << std::setfill('0');
168 sstr << std::setw(2);
169
170 for (int i = 0; i < 16; i++) {
171 if (i == 4 || i == 6 || i == 8 || i == 10) {
172 sstr << '-';
173 }
174
175 sstr << ((int) m_uuid_type[i]);
176 }
177
178 return sstr.str();
179 }
180 else {
181 return to_fourcc(m_type);
182 }
183 }
184
185
parse(BitstreamRange & range)186 heif::Error heif::BoxHeader::parse(BitstreamRange& range)
187 {
188 StreamReader::grow_status status;
189 status = range.wait_for_available_bytes(8);
190 if (status != StreamReader::size_reached) {
191 // TODO: return recoverable error at timeout
192 return Error(heif_error_Invalid_input,
193 heif_suberror_End_of_data);
194 }
195
196 m_size = range.read32();
197 m_type = range.read32();
198
199 m_header_size = 8;
200
201 if (m_size == 1) {
202 status = range.wait_for_available_bytes(8);
203 if (status != StreamReader::size_reached) {
204 // TODO: return recoverable error at timeout
205 return Error(heif_error_Invalid_input,
206 heif_suberror_End_of_data);
207 }
208
209 uint64_t high = range.read32();
210 uint64_t low = range.read32();
211
212 m_size = (high << 32) | low;
213 m_header_size += 8;
214
215 std::stringstream sstr;
216 sstr << "Box size " << m_size << " exceeds security limit.";
217
218 if (m_size > MAX_LARGE_BOX_SIZE) {
219 return Error(heif_error_Memory_allocation_error,
220 heif_suberror_Security_limit_exceeded,
221 sstr.str());
222 }
223 }
224
225 if (m_type == fourcc("uuid")) {
226 status = range.wait_for_available_bytes(16);
227 if (status != StreamReader::size_reached) {
228 // TODO: return recoverable error at timeout
229 return Error(heif_error_Invalid_input,
230 heif_suberror_End_of_data);
231 }
232
233 if (range.prepare_read(16)) {
234 m_uuid_type.resize(16);
235 range.get_istream()->read((char*) m_uuid_type.data(), 16);
236 }
237
238 m_header_size += 16;
239 }
240
241 return range.get_error();
242 }
243
244
reserve_box_header_space(StreamWriter & writer) const245 size_t heif::BoxHeader::reserve_box_header_space(StreamWriter& writer) const
246 {
247 size_t start_pos = writer.get_position();
248
249 int header_size = is_full_box_header() ? (8 + 4) : 8;
250
251 writer.skip(header_size);
252
253 return start_pos;
254 }
255
256
prepend_header(StreamWriter & writer,size_t box_start) const257 heif::Error heif::BoxHeader::prepend_header(StreamWriter& writer, size_t box_start) const
258 {
259 const int reserved_header_size = is_full_box_header() ? (8 + 4) : 8;
260
261
262 // determine header size
263
264 int header_size = 0;
265
266 header_size += 8; // normal header size
267
268 if (is_full_box_header()) {
269 header_size += 4;
270 }
271
272 if (m_type == fourcc("uuid")) {
273 header_size += 16;
274 }
275
276 bool large_size = false;
277
278 size_t data_size = writer.data_size() - box_start - reserved_header_size;
279
280 if (data_size + header_size > 0xFFFFFFFF) {
281 header_size += 8;
282 large_size = true;
283 }
284
285 size_t box_size = data_size + header_size;
286
287
288 // --- write header
289
290 writer.set_position(box_start);
291 assert(header_size >= reserved_header_size);
292 writer.insert(header_size - reserved_header_size);
293
294 if (large_size) {
295 writer.write32(1);
296 }
297 else {
298 assert(box_size <= 0xFFFFFFFF);
299 writer.write32((uint32_t) box_size);
300 }
301
302 writer.write32(m_type);
303
304 if (large_size) {
305 writer.write64(box_size);
306 }
307
308 if (m_type == fourcc("uuid")) {
309 assert(m_uuid_type.size() == 16);
310 writer.write(m_uuid_type);
311 }
312
313 if (is_full_box_header()) {
314 assert((m_flags & ~0x00FFFFFF) == 0);
315
316 writer.write32((m_version << 24) | m_flags);
317 }
318
319 writer.set_position_to_end(); // Note: should we move to the end of the box after writing the header?
320
321 return Error::Ok;
322 }
323
324
dump(Indent & indent) const325 std::string BoxHeader::dump(Indent& indent) const
326 {
327 std::ostringstream sstr;
328 sstr << indent << "Box: " << get_type_string() << " -----\n";
329 sstr << indent << "size: " << get_box_size() << " (header size: " << get_header_size() << ")\n";
330
331 if (m_is_full_box) {
332 sstr << indent << "version: " << ((int) m_version) << "\n"
333 << indent << "flags: " << std::hex << m_flags << "\n";
334 }
335
336 return sstr.str();
337 }
338
339
parse(BitstreamRange & range)340 Error Box::parse(BitstreamRange& range)
341 {
342 // skip box
343
344 if (get_box_size() == size_until_end_of_file) {
345 range.skip_to_end_of_file();
346 }
347 else {
348 uint64_t content_size = get_box_size() - get_header_size();
349 if (range.prepare_read(content_size)) {
350 if (content_size > MAX_BOX_SIZE) {
351 return Error(heif_error_Invalid_input,
352 heif_suberror_Invalid_box_size);
353 }
354
355 range.get_istream()->seek_cur(get_box_size() - get_header_size());
356 }
357 }
358
359 // Note: seekg() clears the eof flag and it will not be set again afterwards,
360 // hence we have to test for the fail flag.
361
362 return range.get_error();
363 }
364
365
parse_full_box_header(BitstreamRange & range)366 Error BoxHeader::parse_full_box_header(BitstreamRange& range)
367 {
368 uint32_t data = range.read32();
369 m_version = static_cast<uint8_t>(data >> 24);
370 m_flags = data & 0x00FFFFFF;
371 m_is_full_box = true;
372
373 m_header_size += 4;
374
375 return range.get_error();
376 }
377
378
read(BitstreamRange & range,std::shared_ptr<heif::Box> * result)379 Error Box::read(BitstreamRange& range, std::shared_ptr<heif::Box>* result)
380 {
381 BoxHeader hdr;
382 Error err = hdr.parse(range);
383 if (err) {
384 return err;
385 }
386
387 if (range.error()) {
388 return range.get_error();
389 }
390
391 std::shared_ptr<Box> box;
392
393 switch (hdr.get_short_type()) {
394 case fourcc("ftyp"):
395 box = std::make_shared<Box_ftyp>(hdr);
396 break;
397
398 case fourcc("meta"):
399 box = std::make_shared<Box_meta>(hdr);
400 break;
401
402 case fourcc("hdlr"):
403 box = std::make_shared<Box_hdlr>(hdr);
404 break;
405
406 case fourcc("pitm"):
407 box = std::make_shared<Box_pitm>(hdr);
408 break;
409
410 case fourcc("iloc"):
411 box = std::make_shared<Box_iloc>(hdr);
412 break;
413
414 case fourcc("iinf"):
415 box = std::make_shared<Box_iinf>(hdr);
416 break;
417
418 case fourcc("infe"):
419 box = std::make_shared<Box_infe>(hdr);
420 break;
421
422 case fourcc("iprp"):
423 box = std::make_shared<Box_iprp>(hdr);
424 break;
425
426 case fourcc("ipco"):
427 box = std::make_shared<Box_ipco>(hdr);
428 break;
429
430 case fourcc("ipma"):
431 box = std::make_shared<Box_ipma>(hdr);
432 break;
433
434 case fourcc("ispe"):
435 box = std::make_shared<Box_ispe>(hdr);
436 break;
437
438 case fourcc("auxC"):
439 box = std::make_shared<Box_auxC>(hdr);
440 break;
441
442 case fourcc("irot"):
443 box = std::make_shared<Box_irot>(hdr);
444 break;
445
446 case fourcc("imir"):
447 box = std::make_shared<Box_imir>(hdr);
448 break;
449
450 case fourcc("clap"):
451 box = std::make_shared<Box_clap>(hdr);
452 break;
453
454 case fourcc("iref"):
455 box = std::make_shared<Box_iref>(hdr);
456 break;
457
458 case fourcc("hvcC"):
459 box = std::make_shared<Box_hvcC>(hdr);
460 break;
461
462 case fourcc("av1C"):
463 box = std::make_shared<Box_av1C>(hdr);
464 break;
465
466 case fourcc("idat"):
467 box = std::make_shared<Box_idat>(hdr);
468 break;
469
470 case fourcc("grpl"):
471 box = std::make_shared<Box_grpl>(hdr);
472 break;
473
474 case fourcc("dinf"):
475 box = std::make_shared<Box_dinf>(hdr);
476 break;
477
478 case fourcc("dref"):
479 box = std::make_shared<Box_dref>(hdr);
480 break;
481
482 case fourcc("url "):
483 box = std::make_shared<Box_url>(hdr);
484 break;
485
486 case fourcc("colr"):
487 box = std::make_shared<Box_colr>(hdr);
488 break;
489
490 case fourcc("pixi"):
491 box = std::make_shared<Box_pixi>(hdr);
492 break;
493
494 default:
495 box = std::make_shared<Box>(hdr);
496 break;
497 }
498
499 if (hdr.get_box_size() < hdr.get_header_size()) {
500 std::stringstream sstr;
501 sstr << "Box size (" << hdr.get_box_size() << " bytes) smaller than header size ("
502 << hdr.get_header_size() << " bytes)";
503
504 // Sanity check.
505 return Error(heif_error_Invalid_input,
506 heif_suberror_Invalid_box_size,
507 sstr.str());
508 }
509
510
511 if (range.get_nesting_level() > MAX_BOX_NESTING_LEVEL) {
512 return Error(heif_error_Memory_allocation_error,
513 heif_suberror_Security_limit_exceeded,
514 "Security limit for maximum nesting of boxes has been exceeded");
515 }
516
517
518 auto status = range.wait_for_available_bytes(hdr.get_box_size() - hdr.get_header_size());
519 if (status != StreamReader::size_reached) {
520 // TODO: return recoverable error at timeout
521 return Error(heif_error_Invalid_input,
522 heif_suberror_End_of_data);
523 }
524
525
526 // Security check: make sure that box size does not exceed int64 size.
527
528 if (hdr.get_box_size() > (uint64_t) std::numeric_limits<int64_t>::max()) {
529 return Error(heif_error_Invalid_input,
530 heif_suberror_Invalid_box_size);
531 }
532
533 int64_t box_size = static_cast<int64_t>(hdr.get_box_size());
534 int64_t box_size_without_header = box_size - hdr.get_header_size();
535
536 // Box size may not be larger than remaining bytes in parent box.
537
538 if (range.get_remaining_bytes() < box_size_without_header) {
539 return Error(heif_error_Invalid_input,
540 heif_suberror_Invalid_box_size);
541 }
542
543
544 // Create child bitstream range and read box from that range.
545
546 BitstreamRange boxrange(range.get_istream(),
547 box_size_without_header,
548 &range);
549
550 err = box->parse(boxrange);
551 if (err == Error::Ok) {
552 *result = std::move(box);
553 }
554
555 boxrange.skip_to_end_of_box();
556
557 return err;
558 }
559
560
dump(Indent & indent) const561 std::string Box::dump(Indent& indent) const
562 {
563 std::ostringstream sstr;
564
565 sstr << BoxHeader::dump(indent);
566
567 return sstr.str();
568 }
569
570
write(StreamWriter & writer) const571 Error Box::write(StreamWriter& writer) const
572 {
573 size_t box_start = reserve_box_header_space(writer);
574
575 Error err = write_children(writer);
576
577 prepend_header(writer, box_start);
578
579 return err;
580 }
581
582
get_child_box(uint32_t short_type) const583 std::shared_ptr<Box> Box::get_child_box(uint32_t short_type) const
584 {
585 for (auto& box : m_children) {
586 if (box->get_short_type() == short_type) {
587 return box;
588 }
589 }
590
591 return nullptr;
592 }
593
594
get_child_boxes(uint32_t short_type) const595 std::vector<std::shared_ptr<Box>> Box::get_child_boxes(uint32_t short_type) const
596 {
597 std::vector<std::shared_ptr<Box>> result;
598 for (auto& box : m_children) {
599 if (box->get_short_type() == short_type) {
600 result.push_back(box);
601 }
602 }
603
604 return result;
605 }
606
607
read_children(BitstreamRange & range,int max_number)608 Error Box::read_children(BitstreamRange& range, int max_number)
609 {
610 int count = 0;
611
612 while (!range.eof() && !range.error()) {
613 std::shared_ptr<Box> box;
614 Error error = Box::read(range, &box);
615 if (error != Error::Ok) {
616 return error;
617 }
618
619 if (m_children.size() > MAX_CHILDREN_PER_BOX) {
620 std::stringstream sstr;
621 sstr << "Maximum number of child boxes " << MAX_CHILDREN_PER_BOX << " exceeded.";
622
623 // Sanity check.
624 return Error(heif_error_Memory_allocation_error,
625 heif_suberror_Security_limit_exceeded,
626 sstr.str());
627 }
628
629 m_children.push_back(std::move(box));
630
631
632 // count the new child and end reading new children when we reached the expected number
633
634 count++;
635
636 if (max_number != READ_CHILDREN_ALL &&
637 count == max_number) {
638 break;
639 }
640 }
641
642 return range.get_error();
643 }
644
645
write_children(StreamWriter & writer) const646 Error Box::write_children(StreamWriter& writer) const
647 {
648 for (const auto& child : m_children) {
649 Error err = child->write(writer);
650 if (err) {
651 return err;
652 }
653 }
654
655 return Error::Ok;
656 }
657
658
dump_children(Indent & indent) const659 std::string Box::dump_children(Indent& indent) const
660 {
661 std::ostringstream sstr;
662
663 bool first = true;
664
665 indent++;
666 for (const auto& childBox : m_children) {
667 if (first) {
668 first = false;
669 }
670 else {
671 sstr << indent << "\n";
672 }
673
674 sstr << childBox->dump(indent);
675 }
676 indent--;
677
678 return sstr.str();
679 }
680
681
derive_box_version_recursive()682 void Box::derive_box_version_recursive()
683 {
684 derive_box_version();
685
686 for (auto& child : m_children) {
687 child->derive_box_version_recursive();
688 }
689 }
690
691
parse(BitstreamRange & range)692 Error Box_ftyp::parse(BitstreamRange& range)
693 {
694 m_major_brand = range.read32();
695 m_minor_version = range.read32();
696
697 if (get_box_size() <= get_header_size() + 8) {
698 // Sanity check.
699 return Error(heif_error_Invalid_input,
700 heif_suberror_Invalid_box_size,
701 "ftyp box too small (less than 8 bytes)");
702 }
703
704 uint64_t n_minor_brands = (get_box_size() - get_header_size() - 8) / 4;
705
706 for (uint64_t i = 0; i < n_minor_brands && !range.error(); i++) {
707 m_compatible_brands.push_back(range.read32());
708 }
709
710 return range.get_error();
711 }
712
713
has_compatible_brand(uint32_t brand) const714 bool Box_ftyp::has_compatible_brand(uint32_t brand) const
715 {
716 for (uint32_t b : m_compatible_brands) {
717 if (b == brand) {
718 return true;
719 }
720 }
721
722 return false;
723 }
724
725
dump(Indent & indent) const726 std::string Box_ftyp::dump(Indent& indent) const
727 {
728 std::ostringstream sstr;
729
730 sstr << BoxHeader::dump(indent);
731
732 sstr << indent << "major brand: " << to_fourcc(m_major_brand) << "\n"
733 << indent << "minor version: " << m_minor_version << "\n"
734 << indent << "compatible brands: ";
735
736 bool first = true;
737 for (uint32_t brand : m_compatible_brands) {
738 if (first) { first = false; }
739 else { sstr << ','; }
740
741 sstr << to_fourcc(brand);
742 }
743 sstr << "\n";
744
745 return sstr.str();
746 }
747
748
add_compatible_brand(uint32_t brand)749 void Box_ftyp::add_compatible_brand(uint32_t brand)
750 {
751 // TODO: check whether brand already exists in the list
752
753 m_compatible_brands.push_back(brand);
754 }
755
756
write(StreamWriter & writer) const757 Error Box_ftyp::write(StreamWriter& writer) const
758 {
759 size_t box_start = reserve_box_header_space(writer);
760
761 writer.write32(m_major_brand);
762 writer.write32(m_minor_version);
763
764 for (uint32_t b : m_compatible_brands) {
765 writer.write32(b);
766 }
767
768 prepend_header(writer, box_start);
769
770 return Error::Ok;
771 }
772
773
parse(BitstreamRange & range)774 Error Box_meta::parse(BitstreamRange& range)
775 {
776 parse_full_box_header(range);
777
778 /*
779 uint64_t boxSizeLimit;
780 if (get_box_size() == BoxHeader::size_until_end_of_file) {
781 boxSizeLimit = sizeLimit;
782 }
783 else {
784 boxSizeLimit = get_box_size() - get_header_size();
785 }
786 */
787
788 return read_children(range);
789 }
790
791
dump(Indent & indent) const792 std::string Box_meta::dump(Indent& indent) const
793 {
794 std::ostringstream sstr;
795 sstr << Box::dump(indent);
796 sstr << dump_children(indent);
797
798 return sstr.str();
799 }
800
801
parse(BitstreamRange & range)802 Error Box_hdlr::parse(BitstreamRange& range)
803 {
804 parse_full_box_header(range);
805
806 m_pre_defined = range.read32();
807 m_handler_type = range.read32();
808
809 for (int i = 0; i < 3; i++) {
810 m_reserved[i] = range.read32();
811 }
812
813 m_name = range.read_string();
814
815 return range.get_error();
816 }
817
818
dump(Indent & indent) const819 std::string Box_hdlr::dump(Indent& indent) const
820 {
821 std::ostringstream sstr;
822 sstr << Box::dump(indent);
823 sstr << indent << "pre_defined: " << m_pre_defined << "\n"
824 << indent << "handler_type: " << to_fourcc(m_handler_type) << "\n"
825 << indent << "name: " << m_name << "\n";
826
827 return sstr.str();
828 }
829
830
write(StreamWriter & writer) const831 Error Box_hdlr::write(StreamWriter& writer) const
832 {
833 size_t box_start = reserve_box_header_space(writer);
834
835 writer.write32(m_pre_defined);
836 writer.write32(m_handler_type);
837
838 for (int i = 0; i < 3; i++) {
839 writer.write32(m_reserved[i]);
840 }
841
842 writer.write(m_name);
843
844 prepend_header(writer, box_start);
845
846 return Error::Ok;
847 }
848
849
parse(BitstreamRange & range)850 Error Box_pitm::parse(BitstreamRange& range)
851 {
852 parse_full_box_header(range);
853
854 if (get_version() == 0) {
855 m_item_ID = range.read16();
856 }
857 else {
858 m_item_ID = range.read32();
859 }
860
861 return range.get_error();
862 }
863
864
dump(Indent & indent) const865 std::string Box_pitm::dump(Indent& indent) const
866 {
867 std::ostringstream sstr;
868 sstr << Box::dump(indent);
869 sstr << indent << "item_ID: " << m_item_ID << "\n";
870
871 return sstr.str();
872 }
873
874
derive_box_version()875 void Box_pitm::derive_box_version()
876 {
877 if (m_item_ID <= 0xFFFF) {
878 set_version(0);
879 }
880 else {
881 set_version(1);
882 }
883 }
884
885
write(StreamWriter & writer) const886 Error Box_pitm::write(StreamWriter& writer) const
887 {
888 size_t box_start = reserve_box_header_space(writer);
889
890 if (get_version() == 0) {
891 assert(m_item_ID <= 0xFFFF);
892 writer.write16((uint16_t) m_item_ID);
893 }
894 else {
895 writer.write32(m_item_ID);
896 }
897
898 prepend_header(writer, box_start);
899
900 return Error::Ok;
901 }
902
903
parse(BitstreamRange & range)904 Error Box_iloc::parse(BitstreamRange& range)
905 {
906 /*
907 printf("box size: %d\n",get_box_size());
908 printf("header size: %d\n",get_header_size());
909 printf("start limit: %d\n",sizeLimit);
910 */
911
912 parse_full_box_header(range);
913
914 uint16_t values4 = range.read16();
915
916 int offset_size = (values4 >> 12) & 0xF;
917 int length_size = (values4 >> 8) & 0xF;
918 int base_offset_size = (values4 >> 4) & 0xF;
919 int index_size = 0;
920
921 if (get_version() > 1) {
922 index_size = (values4 & 0xF);
923 }
924
925 int item_count;
926 if (get_version() < 2) {
927 item_count = range.read16();
928 }
929 else {
930 item_count = range.read32();
931 }
932
933 // Sanity check.
934 if (item_count > MAX_ILOC_ITEMS) {
935 std::stringstream sstr;
936 sstr << "iloc box contains " << item_count << " items, which exceeds the security limit of "
937 << MAX_ILOC_ITEMS << " items.";
938
939 return Error(heif_error_Memory_allocation_error,
940 heif_suberror_Security_limit_exceeded,
941 sstr.str());
942 }
943
944 for (int i = 0; i < item_count; i++) {
945 Item item;
946
947 if (get_version() < 2) {
948 item.item_ID = range.read16();
949 }
950 else {
951 item.item_ID = range.read32();
952 }
953
954 if (get_version() >= 1) {
955 values4 = range.read16();
956 item.construction_method = (values4 & 0xF);
957 }
958
959 item.data_reference_index = range.read16();
960
961 item.base_offset = 0;
962 if (base_offset_size == 4) {
963 item.base_offset = range.read32();
964 }
965 else if (base_offset_size == 8) {
966 item.base_offset = ((uint64_t) range.read32()) << 32;
967 item.base_offset |= range.read32();
968 }
969
970 int extent_count = range.read16();
971 // Sanity check.
972 if (extent_count > MAX_ILOC_EXTENTS_PER_ITEM) {
973 std::stringstream sstr;
974 sstr << "Number of extents in iloc box (" << extent_count << ") exceeds security limit ("
975 << MAX_ILOC_EXTENTS_PER_ITEM << ")\n";
976
977 return Error(heif_error_Memory_allocation_error,
978 heif_suberror_Security_limit_exceeded,
979 sstr.str());
980 }
981
982 for (int e = 0; e < extent_count; e++) {
983 Extent extent;
984
985 if (get_version() > 1 && index_size > 0) {
986 if (index_size == 4) {
987 extent.index = range.read32();
988 }
989 else if (index_size == 8) {
990 extent.index = ((uint64_t) range.read32()) << 32;
991 extent.index |= range.read32();
992 }
993 }
994
995 extent.offset = 0;
996 if (offset_size == 4) {
997 extent.offset = range.read32();
998 }
999 else if (offset_size == 8) {
1000 extent.offset = ((uint64_t) range.read32()) << 32;
1001 extent.offset |= range.read32();
1002 }
1003
1004
1005 extent.length = 0;
1006 if (length_size == 4) {
1007 extent.length = range.read32();
1008 }
1009 else if (length_size == 8) {
1010 extent.length = ((uint64_t) range.read32()) << 32;
1011 extent.length |= range.read32();
1012 }
1013
1014 item.extents.push_back(extent);
1015 }
1016
1017 if (!range.error()) {
1018 m_items.push_back(item);
1019 }
1020 }
1021
1022 //printf("end limit: %d\n",sizeLimit);
1023
1024 return range.get_error();
1025 }
1026
1027
dump(Indent & indent) const1028 std::string Box_iloc::dump(Indent& indent) const
1029 {
1030 std::ostringstream sstr;
1031 sstr << Box::dump(indent);
1032
1033 for (const Item& item : m_items) {
1034 sstr << indent << "item ID: " << item.item_ID << "\n"
1035 << indent << " construction method: " << ((int) item.construction_method) << "\n"
1036 << indent << " data_reference_index: " << std::hex
1037 << item.data_reference_index << std::dec << "\n"
1038 << indent << " base_offset: " << item.base_offset << "\n";
1039
1040 sstr << indent << " extents: ";
1041 for (const Extent& extent : item.extents) {
1042 sstr << extent.offset << "," << extent.length;
1043 if (extent.index != 0) {
1044 sstr << ";index=" << extent.index;
1045 }
1046 sstr << " ";
1047 }
1048 sstr << "\n";
1049 }
1050
1051 return sstr.str();
1052 }
1053
1054
read_data(const Item & item,std::shared_ptr<StreamReader> istr,const std::shared_ptr<Box_idat> & idat,std::vector<uint8_t> * dest) const1055 Error Box_iloc::read_data(const Item& item,
1056 std::shared_ptr<StreamReader> istr,
1057 const std::shared_ptr<Box_idat>& idat,
1058 std::vector<uint8_t>* dest) const
1059 {
1060 //istr.clear();
1061
1062 for (const auto& extent : item.extents) {
1063 if (item.construction_method == 0) {
1064
1065 // --- security check that we do not allocate too much memory
1066
1067 size_t old_size = dest->size();
1068 if (MAX_MEMORY_BLOCK_SIZE - old_size < extent.length) {
1069 std::stringstream sstr;
1070 sstr << "iloc box contained " << extent.length << " bytes, total memory size would be "
1071 << (old_size + extent.length) << " bytes, exceeding the security limit of "
1072 << MAX_MEMORY_BLOCK_SIZE << " bytes";
1073
1074 return Error(heif_error_Memory_allocation_error,
1075 heif_suberror_Security_limit_exceeded,
1076 sstr.str());
1077 }
1078
1079
1080 // --- make sure that all data is available
1081
1082 if (extent.offset > MAX_FILE_POS ||
1083 item.base_offset > MAX_FILE_POS ||
1084 extent.length > MAX_FILE_POS) {
1085 return Error(heif_error_Invalid_input,
1086 heif_suberror_Security_limit_exceeded,
1087 "iloc data pointers out of allowed range");
1088 }
1089
1090 StreamReader::grow_status status = istr->wait_for_file_size(extent.offset + item.base_offset + extent.length);
1091 if (status == StreamReader::size_beyond_eof) {
1092 // Out-of-bounds
1093 // TODO: I think we should not clear this. Maybe we want to try reading again later and
1094 // hence should not lose the data already read.
1095 dest->clear();
1096
1097 std::stringstream sstr;
1098 sstr << "Extent in iloc box references data outside of file bounds "
1099 << "(points to file position " << extent.offset + item.base_offset << ")\n";
1100
1101 return Error(heif_error_Invalid_input,
1102 heif_suberror_End_of_data,
1103 sstr.str());
1104 }
1105 else if (status == StreamReader::timeout) {
1106 // TODO: maybe we should introduce some 'Recoverable error' instead of 'Invalid input'
1107 return Error(heif_error_Invalid_input,
1108 heif_suberror_End_of_data);
1109 }
1110
1111 // --- move file pointer to start of data
1112
1113 bool success = istr->seek(extent.offset + item.base_offset);
1114 assert(success);
1115 (void) success;
1116
1117
1118 // --- read data
1119
1120 dest->resize(static_cast<size_t>(old_size + extent.length));
1121 success = istr->read((char*) dest->data() + old_size, static_cast<size_t>(extent.length));
1122 assert(success);
1123 }
1124 else if (item.construction_method == 1) {
1125 if (!idat) {
1126 return Error(heif_error_Invalid_input,
1127 heif_suberror_No_idat_box,
1128 "idat box referenced in iref box is not present in file");
1129 }
1130
1131 idat->read_data(istr,
1132 extent.offset + item.base_offset,
1133 extent.length,
1134 *dest);
1135 }
1136 else {
1137 std::stringstream sstr;
1138 sstr << "Item construction method " << item.construction_method << " not implemented";
1139 return Error(heif_error_Unsupported_feature,
1140 heif_suberror_No_idat_box,
1141 sstr.str());
1142 }
1143 }
1144
1145 return Error::Ok;
1146 }
1147
1148
append_data(heif_item_id item_ID,const std::vector<uint8_t> & data,uint8_t construction_method)1149 Error Box_iloc::append_data(heif_item_id item_ID,
1150 const std::vector<uint8_t>& data,
1151 uint8_t construction_method)
1152 {
1153 // check whether this item ID already exists
1154
1155 size_t idx;
1156 for (idx = 0; idx < m_items.size(); idx++) {
1157 if (m_items[idx].item_ID == item_ID) {
1158 break;
1159 }
1160 }
1161
1162 // item does not exist -> add a new one to the end
1163
1164 if (idx == m_items.size()) {
1165 Item item;
1166 item.item_ID = item_ID;
1167 item.construction_method = construction_method;
1168
1169 m_items.push_back(item);
1170 }
1171
1172 if (m_items[idx].construction_method != construction_method) {
1173 // TODO: return error: construction methods do not match
1174 }
1175
1176 Extent extent;
1177 extent.data = data;
1178
1179 if (construction_method==1) {
1180 extent.offset = m_idat_offset;
1181 extent.length = data.size();
1182
1183 m_idat_offset += (int)data.size();
1184 }
1185
1186 m_items[idx].extents.push_back(std::move(extent));
1187
1188 return Error::Ok;
1189 }
1190
1191
derive_box_version()1192 void Box_iloc::derive_box_version()
1193 {
1194 int min_version = m_user_defined_min_version;
1195
1196 if (m_items.size() > 0xFFFF) {
1197 min_version = std::max(min_version, 2);
1198 }
1199
1200 m_offset_size = 0;
1201 m_length_size = 0;
1202 m_base_offset_size = 0;
1203 m_index_size = 0;
1204
1205 for (const auto& item : m_items) {
1206 // check item_ID size
1207 if (item.item_ID > 0xFFFF) {
1208 min_version = std::max(min_version, 2);
1209 }
1210
1211 // check construction method
1212 if (item.construction_method != 0) {
1213 min_version = std::max(min_version, 1);
1214 }
1215
1216 // base offset size
1217 /*
1218 if (item.base_offset > 0xFFFFFFFF) {
1219 m_base_offset_size = 8;
1220 }
1221 else if (item.base_offset > 0) {
1222 m_base_offset_size = 4;
1223 }
1224 */
1225
1226 /*
1227 for (const auto& extent : item.extents) {
1228 // extent index size
1229
1230 if (extent.index != 0) {
1231 min_version = std::max(min_version, 1);
1232 m_index_size = 4;
1233 }
1234
1235 if (extent.index > 0xFFFFFFFF) {
1236 m_index_size = 8;
1237 }
1238
1239 // extent offset size
1240 if (extent.offset > 0xFFFFFFFF) {
1241 m_offset_size = 8;
1242 }
1243 else {
1244 m_offset_size = 4;
1245 }
1246
1247 // extent length size
1248 if (extent.length > 0xFFFFFFFF) {
1249 m_length_size = 8;
1250 }
1251 else {
1252 m_length_size = 4;
1253 }
1254 }
1255 */
1256 }
1257
1258 m_offset_size = 4;
1259 m_length_size = 4;
1260 m_base_offset_size = 4; // TODO: or could be 8 if we write >4GB files
1261 m_index_size = 0;
1262
1263 set_version((uint8_t) min_version);
1264 }
1265
1266
write(StreamWriter & writer) const1267 Error Box_iloc::write(StreamWriter& writer) const
1268 {
1269 // --- write idat
1270
1271 size_t sum_idat_size = 0;
1272
1273 for (const auto& item : m_items) {
1274 if (item.construction_method == 1) {
1275 for (const auto& extent : item.extents) {
1276 sum_idat_size += extent.data.size();
1277 }
1278 }
1279 }
1280
1281 if (sum_idat_size > 0) {
1282 writer.write32((uint32_t) (sum_idat_size + 8));
1283 writer.write32(fourcc("idat"));
1284
1285 for (auto& item : m_items) {
1286 if (item.construction_method == 1) {
1287 for (auto& extent : item.extents) {
1288 writer.write(extent.data);
1289 }
1290 }
1291 }
1292 }
1293
1294
1295 // --- write iloc box
1296
1297 size_t box_start = reserve_box_header_space(writer);
1298
1299 m_iloc_box_start = writer.get_position();
1300
1301 int nSkip = 0;
1302
1303 nSkip += 2;
1304 nSkip += (get_version() < 2) ? 2 : 4; // item_count
1305
1306 for (const auto& item : m_items) {
1307 nSkip += (get_version() < 2) ? 2 : 4; // item_ID
1308 nSkip += (get_version() >= 1) ? 2 : 0; // construction method
1309 nSkip += 4 + m_base_offset_size;
1310
1311 for (const auto& extent : item.extents) {
1312 (void) extent;
1313
1314 if (get_version() >= 1) {
1315 nSkip += m_index_size;
1316 }
1317
1318 nSkip += m_offset_size + m_length_size;
1319 }
1320 }
1321
1322 writer.skip(nSkip);
1323 prepend_header(writer, box_start);
1324
1325 return Error::Ok;
1326 }
1327
1328
write_mdat_after_iloc(StreamWriter & writer)1329 Error Box_iloc::write_mdat_after_iloc(StreamWriter& writer)
1330 {
1331 // --- compute sum of all mdat data
1332
1333 size_t sum_mdat_size = 0;
1334
1335 for (const auto& item : m_items) {
1336 if (item.construction_method == 0) {
1337 for (const auto& extent : item.extents) {
1338 sum_mdat_size += extent.data.size();
1339 }
1340 }
1341 }
1342
1343 if (sum_mdat_size > 0xFFFFFFFF) {
1344 // TODO: box size > 4 GB
1345 }
1346
1347
1348 // --- write mdat box
1349
1350 writer.write32((uint32_t) (sum_mdat_size + 8));
1351 writer.write32(fourcc("mdat"));
1352
1353 for (auto& item : m_items) {
1354 if (item.construction_method == 0) {
1355 item.base_offset = writer.get_position();
1356
1357 for (auto& extent : item.extents) {
1358 extent.offset = writer.get_position() - item.base_offset;
1359 extent.length = extent.data.size();
1360
1361 writer.write(extent.data);
1362 }
1363 }
1364 }
1365
1366
1367 // --- patch iloc box
1368
1369 patch_iloc_header(writer);
1370
1371 return Error::Ok;
1372 }
1373
1374
patch_iloc_header(StreamWriter & writer) const1375 void Box_iloc::patch_iloc_header(StreamWriter& writer) const
1376 {
1377 size_t old_pos = writer.get_position();
1378 writer.set_position(m_iloc_box_start);
1379
1380 writer.write8((uint8_t) ((m_offset_size << 4) | (m_length_size)));
1381 writer.write8((uint8_t) ((m_base_offset_size << 4) | (m_index_size)));
1382
1383 if (get_version() < 2) {
1384 writer.write16((uint16_t) m_items.size());
1385 }
1386 else {
1387 writer.write32((uint32_t) m_items.size());
1388 }
1389
1390 for (const auto& item : m_items) {
1391 if (get_version() < 2) {
1392 writer.write16((uint16_t) item.item_ID);
1393 }
1394 else {
1395 writer.write32((uint32_t) item.item_ID);
1396 }
1397
1398 if (get_version() >= 1) {
1399 writer.write16(item.construction_method);
1400 }
1401
1402 writer.write16(item.data_reference_index);
1403 writer.write(m_base_offset_size, item.base_offset);
1404 writer.write16((uint16_t) item.extents.size());
1405
1406 for (const auto& extent : item.extents) {
1407 if (get_version() >= 1 && m_index_size > 0) {
1408 writer.write(m_index_size, extent.index);
1409 }
1410
1411 writer.write(m_offset_size, extent.offset);
1412 writer.write(m_length_size, extent.length);
1413 }
1414 }
1415
1416 writer.set_position(old_pos);
1417 }
1418
1419
parse(BitstreamRange & range)1420 Error Box_infe::parse(BitstreamRange& range)
1421 {
1422 parse_full_box_header(range);
1423
1424 if (get_version() <= 1) {
1425 m_item_ID = range.read16();
1426 m_item_protection_index = range.read16();
1427
1428 m_item_name = range.read_string();
1429 m_content_type = range.read_string();
1430 m_content_encoding = range.read_string();
1431 }
1432
1433 if (get_version() >= 2) {
1434 m_hidden_item = (get_flags() & 1);
1435
1436 if (get_version() == 2) {
1437 m_item_ID = range.read16();
1438 }
1439 else {
1440 m_item_ID = range.read32();
1441 }
1442
1443 m_item_protection_index = range.read16();
1444 uint32_t item_type = range.read32();
1445 if (item_type != 0) {
1446 m_item_type = to_fourcc(item_type);
1447 }
1448
1449 m_item_name = range.read_string();
1450 if (item_type == fourcc("mime")) {
1451 m_content_type = range.read_string();
1452 m_content_encoding = range.read_string();
1453 }
1454 else if (item_type == fourcc("uri ")) {
1455 m_item_uri_type = range.read_string();
1456 }
1457 }
1458
1459 return range.get_error();
1460 }
1461
1462
derive_box_version()1463 void Box_infe::derive_box_version()
1464 {
1465 int min_version = 0;
1466
1467 if (m_hidden_item) {
1468 min_version = std::max(min_version, 2);
1469 }
1470
1471 if (m_item_ID > 0xFFFF) {
1472 min_version = std::max(min_version, 3);
1473 }
1474
1475
1476 if (m_item_type != "") {
1477 min_version = std::max(min_version, 2);
1478 }
1479
1480 set_version((uint8_t) min_version);
1481 }
1482
1483
set_hidden_item(bool hidden)1484 void Box_infe::set_hidden_item(bool hidden)
1485 {
1486 m_hidden_item = hidden;
1487
1488 if (m_hidden_item) {
1489 set_flags(get_flags() | 1);
1490 }
1491 else {
1492 set_flags(get_flags() & ~1);
1493 }
1494 }
1495
write(StreamWriter & writer) const1496 Error Box_infe::write(StreamWriter& writer) const
1497 {
1498 size_t box_start = reserve_box_header_space(writer);
1499
1500 if (get_version() <= 1) {
1501 writer.write16((uint16_t) m_item_ID);
1502 writer.write16(m_item_protection_index);
1503
1504 writer.write(m_item_name);
1505 writer.write(m_content_type);
1506 writer.write(m_content_encoding);
1507 }
1508
1509 if (get_version() >= 2) {
1510 if (get_version() == 2) {
1511 writer.write16((uint16_t) m_item_ID);
1512 }
1513 else if (get_version() == 3) {
1514 writer.write32(m_item_ID);
1515 }
1516
1517 writer.write16(m_item_protection_index);
1518
1519 if (m_item_type.empty()) {
1520 writer.write32(0);
1521 }
1522 else {
1523 writer.write32(from_fourcc(m_item_type.c_str()));
1524 }
1525
1526 writer.write(m_item_name);
1527 if (m_item_type == "mime") {
1528 writer.write(m_content_type);
1529 writer.write(m_content_encoding);
1530 }
1531 else if (m_item_type == "uri ") {
1532 writer.write(m_item_uri_type);
1533 }
1534 }
1535
1536 prepend_header(writer, box_start);
1537
1538 return Error::Ok;
1539 }
1540
1541
dump(Indent & indent) const1542 std::string Box_infe::dump(Indent& indent) const
1543 {
1544 std::ostringstream sstr;
1545 sstr << Box::dump(indent);
1546
1547 sstr << indent << "item_ID: " << m_item_ID << "\n"
1548 << indent << "item_protection_index: " << m_item_protection_index << "\n"
1549 << indent << "item_type: " << m_item_type << "\n"
1550 << indent << "item_name: " << m_item_name << "\n"
1551 << indent << "content_type: " << m_content_type << "\n"
1552 << indent << "content_encoding: " << m_content_encoding << "\n"
1553 << indent << "item uri type: " << m_item_uri_type << "\n"
1554 << indent << "hidden item: " << std::boolalpha << m_hidden_item << "\n";
1555
1556 return sstr.str();
1557 }
1558
1559
parse(BitstreamRange & range)1560 Error Box_iinf::parse(BitstreamRange& range)
1561 {
1562 parse_full_box_header(range);
1563
1564 int nEntries_size = (get_version() > 0) ? 4 : 2;
1565
1566 int item_count;
1567 if (nEntries_size == 2) {
1568 item_count = range.read16();
1569 }
1570 else {
1571 item_count = range.read32();
1572 }
1573
1574 if (item_count == 0) {
1575 return Error::Ok;
1576 }
1577
1578 // TODO: Only try to read "item_count" children.
1579 return read_children(range);
1580 }
1581
1582
dump(Indent & indent) const1583 std::string Box_iinf::dump(Indent& indent) const
1584 {
1585 std::ostringstream sstr;
1586 sstr << Box::dump(indent);
1587
1588 sstr << dump_children(indent);
1589
1590 return sstr.str();
1591 }
1592
1593
parse(BitstreamRange & range)1594 Error Box_iprp::parse(BitstreamRange& range)
1595 {
1596 //parse_full_box_header(range);
1597
1598 return read_children(range);
1599 }
1600
1601
derive_box_version()1602 void Box_iinf::derive_box_version()
1603 {
1604 if (m_children.size() > 0xFFFF) {
1605 set_version(1);
1606 }
1607 else {
1608 set_version(0);
1609 }
1610 }
1611
1612
write(StreamWriter & writer) const1613 Error Box_iinf::write(StreamWriter& writer) const
1614 {
1615 size_t box_start = reserve_box_header_space(writer);
1616
1617 int nEntries_size = (get_version() > 0) ? 4 : 2;
1618
1619 writer.write(nEntries_size, m_children.size());
1620
1621
1622 Error err = write_children(writer);
1623
1624 prepend_header(writer, box_start);
1625
1626 return err;
1627 }
1628
1629
dump(Indent & indent) const1630 std::string Box_iprp::dump(Indent& indent) const
1631 {
1632 std::ostringstream sstr;
1633 sstr << Box::dump(indent);
1634
1635 sstr << dump_children(indent);
1636
1637 return sstr.str();
1638 }
1639
1640
parse(BitstreamRange & range)1641 Error Box_ipco::parse(BitstreamRange& range)
1642 {
1643 //parse_full_box_header(range);
1644
1645 return read_children(range);
1646 }
1647
1648
dump(Indent & indent) const1649 std::string Box_ipco::dump(Indent& indent) const
1650 {
1651 std::ostringstream sstr;
1652 sstr << Box::dump(indent);
1653
1654 sstr << dump_children(indent);
1655
1656 return sstr.str();
1657 }
1658
parse(BitstreamRange & range)1659 Error color_profile_nclx::parse(BitstreamRange& range)
1660 {
1661 StreamReader::grow_status status;
1662 status = range.wait_for_available_bytes(7);
1663 if (status != StreamReader::size_reached) {
1664 // TODO: return recoverable error at timeout
1665 return Error(heif_error_Invalid_input,
1666 heif_suberror_End_of_data);
1667 }
1668
1669 m_colour_primaries = range.read16();
1670 m_transfer_characteristics = range.read16();
1671 m_matrix_coefficients = range.read16();
1672 m_full_range_flag = (range.read8() & 0x80 ? true : false);
1673
1674 return Error::Ok;
1675 }
1676
get_nclx_color_profile(struct heif_color_profile_nclx ** out_data) const1677 Error color_profile_nclx::get_nclx_color_profile(struct heif_color_profile_nclx** out_data) const
1678 {
1679 *out_data = alloc_nclx_color_profile();
1680
1681 if (*out_data == nullptr) {
1682 return Error(heif_error_Memory_allocation_error,
1683 heif_suberror_Unspecified);
1684 }
1685
1686 struct heif_color_profile_nclx* nclx = *out_data;
1687
1688 nclx->version = 1;
1689 nclx->color_primaries = (enum heif_color_primaries) get_colour_primaries();
1690 nclx->transfer_characteristics = (enum heif_transfer_characteristics) get_transfer_characteristics();
1691 nclx->matrix_coefficients = (enum heif_matrix_coefficients) get_matrix_coefficients();
1692 nclx->full_range_flag = get_full_range_flag();
1693
1694 // fill color primaries
1695
1696 auto primaries = ::get_colour_primaries(nclx->color_primaries);
1697
1698 nclx->color_primary_red_x = primaries.redX;
1699 nclx->color_primary_red_y = primaries.redY;
1700 nclx->color_primary_green_x = primaries.greenX;
1701 nclx->color_primary_green_y = primaries.greenY;
1702 nclx->color_primary_blue_x = primaries.blueX;
1703 nclx->color_primary_blue_y = primaries.blueY;
1704 nclx->color_primary_white_x = primaries.whiteX;
1705 nclx->color_primary_white_y = primaries.whiteY;
1706
1707 return Error::Ok;
1708 }
1709
1710
alloc_nclx_color_profile()1711 struct heif_color_profile_nclx* color_profile_nclx::alloc_nclx_color_profile()
1712 {
1713 auto profile = (heif_color_profile_nclx*) malloc(sizeof(struct heif_color_profile_nclx));
1714
1715 if (profile) {
1716 profile->version = 1;
1717 profile->color_primaries = heif_color_primaries_unspecified;
1718 profile->transfer_characteristics = heif_transfer_characteristic_unspecified;
1719 profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6;
1720 profile->full_range_flag = true;
1721 }
1722
1723 return profile;
1724 }
1725
1726
free_nclx_color_profile(struct heif_color_profile_nclx * profile)1727 void color_profile_nclx::free_nclx_color_profile(struct heif_color_profile_nclx* profile)
1728 {
1729 free(profile);
1730 }
1731
1732
set_default()1733 void color_profile_nclx::set_default()
1734 {
1735 m_colour_primaries = 2;
1736 m_transfer_characteristics = 2;
1737 m_matrix_coefficients = 6;
1738 m_full_range_flag = true;
1739 }
1740
1741
set_undefined()1742 void color_profile_nclx::set_undefined()
1743 {
1744 m_colour_primaries = 2;
1745 m_transfer_characteristics = 2;
1746 m_matrix_coefficients = 2;
1747 m_full_range_flag = true;
1748 }
1749
1750
set_from_heif_color_profile_nclx(const struct heif_color_profile_nclx * nclx)1751 void color_profile_nclx::set_from_heif_color_profile_nclx(const struct heif_color_profile_nclx* nclx)
1752 {
1753 m_colour_primaries = nclx->color_primaries;
1754 m_transfer_characteristics = nclx->transfer_characteristics;
1755 m_matrix_coefficients = nclx->matrix_coefficients;
1756 m_full_range_flag = nclx->full_range_flag;
1757 }
1758
1759
parse(BitstreamRange & range)1760 Error Box_colr::parse(BitstreamRange& range)
1761 {
1762 StreamReader::grow_status status;
1763 uint32_t colour_type = range.read32();
1764
1765 if (colour_type == fourcc("nclx")) {
1766 auto color_profile = std::make_shared<color_profile_nclx>();
1767 m_color_profile = color_profile;
1768 Error err = color_profile->parse(range);
1769 if (err) {
1770 return err;
1771 }
1772 }
1773 else if (colour_type == fourcc("prof") ||
1774 colour_type == fourcc("rICC")) {
1775 auto profile_size = get_box_size() - get_header_size() - 4;
1776 status = range.wait_for_available_bytes(profile_size);
1777 if (status != StreamReader::size_reached) {
1778 // TODO: return recoverable error at timeout
1779 return Error(heif_error_Invalid_input,
1780 heif_suberror_End_of_data);
1781 }
1782
1783 std::vector<uint8_t> rawData(profile_size);
1784 for (size_t i = 0; i < profile_size; i++) {
1785 rawData[i] = range.read8();
1786 }
1787
1788 m_color_profile = std::make_shared<color_profile_raw>(colour_type, rawData);
1789 }
1790 else {
1791 return Error(heif_error_Invalid_input,
1792 heif_suberror_Unknown_color_profile_type);
1793 }
1794
1795 return range.get_error();
1796 }
1797
1798
dump(Indent & indent) const1799 std::string Box_colr::dump(Indent& indent) const
1800 {
1801 std::ostringstream sstr;
1802 sstr << Box::dump(indent);
1803
1804 sstr << indent << "colour_type: " << to_fourcc(get_color_profile_type()) << "\n";
1805
1806 if (m_color_profile) {
1807 sstr << m_color_profile->dump(indent);
1808 }
1809 else {
1810 sstr << "no color profile\n";
1811 }
1812
1813 return sstr.str();
1814 }
1815
1816
dump(Indent & indent) const1817 std::string color_profile_raw::dump(Indent& indent) const
1818 {
1819 std::ostringstream sstr;
1820 sstr << indent << "profile size: " << m_data.size() << "\n";
1821 return sstr.str();
1822 }
1823
1824
dump(Indent & indent) const1825 std::string color_profile_nclx::dump(Indent& indent) const
1826 {
1827 std::ostringstream sstr;
1828 sstr << indent << "colour_primaries: " << m_colour_primaries << "\n"
1829 << indent << "transfer_characteristics: " << m_transfer_characteristics << "\n"
1830 << indent << "matrix_coefficients: " << m_matrix_coefficients << "\n"
1831 << indent << "full_range_flag: " << m_full_range_flag << "\n";
1832 return sstr.str();
1833 }
1834
1835
write(StreamWriter & writer) const1836 Error color_profile_nclx::write(StreamWriter& writer) const
1837 {
1838 writer.write16(m_colour_primaries);
1839 writer.write16(m_transfer_characteristics);
1840 writer.write16(m_matrix_coefficients);
1841 writer.write8(m_full_range_flag ? 0x80 : 0x00);
1842
1843 return Error::Ok;
1844 }
1845
write(StreamWriter & writer) const1846 Error color_profile_raw::write(StreamWriter& writer) const
1847 {
1848 writer.write(m_data);
1849
1850 return Error::Ok;
1851 }
1852
write(StreamWriter & writer) const1853 Error Box_colr::write(StreamWriter& writer) const
1854 {
1855 size_t box_start = reserve_box_header_space(writer);
1856
1857 assert(m_color_profile);
1858
1859 writer.write32(m_color_profile->get_type());
1860
1861 Error err = m_color_profile->write(writer);
1862 if (err) {
1863 return err;
1864 }
1865
1866 prepend_header(writer, box_start);
1867
1868 return Error::Ok;
1869 }
1870
1871
parse(BitstreamRange & range)1872 Error Box_pixi::parse(BitstreamRange& range)
1873 {
1874 parse_full_box_header(range);
1875
1876 StreamReader::grow_status status;
1877 uint8_t num_channels = range.read8();
1878 status = range.wait_for_available_bytes(num_channels);
1879 if (status != StreamReader::size_reached) {
1880 // TODO: return recoverable error at timeout
1881 return Error(heif_error_Invalid_input,
1882 heif_suberror_End_of_data);
1883 }
1884
1885 m_bits_per_channel.resize(num_channels);
1886 for (int i = 0; i < num_channels; i++) {
1887 m_bits_per_channel[i] = range.read8();
1888 }
1889
1890 return range.get_error();
1891 }
1892
1893
dump(Indent & indent) const1894 std::string Box_pixi::dump(Indent& indent) const
1895 {
1896 std::ostringstream sstr;
1897 sstr << Box::dump(indent);
1898
1899 sstr << indent << "bits_per_channel: ";
1900
1901 for (size_t i = 0; i < m_bits_per_channel.size(); i++) {
1902 if (i > 0) sstr << ",";
1903 sstr << ((int) m_bits_per_channel[i]);
1904 }
1905
1906 sstr << "\n";
1907
1908 return sstr.str();
1909 }
1910
1911
write(StreamWriter & writer) const1912 Error Box_pixi::write(StreamWriter& writer) const
1913 {
1914 size_t box_start = reserve_box_header_space(writer);
1915
1916 if (m_bits_per_channel.size() > 255 ||
1917 m_bits_per_channel.empty()) {
1918 // TODO: error
1919 assert(false);
1920 }
1921
1922 writer.write8((uint8_t) (m_bits_per_channel.size()));
1923 for (size_t i = 0; i < m_bits_per_channel.size(); i++) {
1924 writer.write8(m_bits_per_channel[i]);
1925 }
1926
1927 prepend_header(writer, box_start);
1928
1929 return Error::Ok;
1930 }
1931
1932
get_properties_for_item_ID(uint32_t itemID,const std::shared_ptr<class Box_ipma> & ipma,std::vector<Property> & out_properties) const1933 Error Box_ipco::get_properties_for_item_ID(uint32_t itemID,
1934 const std::shared_ptr<class Box_ipma>& ipma,
1935 std::vector<Property>& out_properties) const
1936 {
1937 const std::vector<Box_ipma::PropertyAssociation>* property_assoc = ipma->get_properties_for_item_ID(itemID);
1938 if (property_assoc == nullptr) {
1939 std::stringstream sstr;
1940 sstr << "Item (ID=" << itemID << ") has no properties assigned to it in ipma box";
1941
1942 return Error(heif_error_Invalid_input,
1943 heif_suberror_No_properties_assigned_to_item,
1944 sstr.str());
1945 }
1946
1947 auto allProperties = get_all_child_boxes();
1948 for (const Box_ipma::PropertyAssociation& assoc : *property_assoc) {
1949 if (assoc.property_index > allProperties.size()) {
1950 std::stringstream sstr;
1951 sstr << "Nonexisting property (index=" << assoc.property_index << ") for item "
1952 << " ID=" << itemID << " referenced in ipma box";
1953
1954 return Error(heif_error_Invalid_input,
1955 heif_suberror_Ipma_box_references_nonexisting_property,
1956 sstr.str());
1957 }
1958
1959 Property prop;
1960 prop.essential = assoc.essential;
1961
1962 if (assoc.property_index > 0) {
1963 prop.property = allProperties[assoc.property_index - 1];
1964 out_properties.push_back(prop);
1965 }
1966 }
1967
1968 return Error::Ok;
1969 }
1970
1971
get_property_for_item_ID(heif_item_id itemID,const std::shared_ptr<class Box_ipma> & ipma,uint32_t box_type) const1972 std::shared_ptr<Box> Box_ipco::get_property_for_item_ID(heif_item_id itemID,
1973 const std::shared_ptr<class Box_ipma>& ipma,
1974 uint32_t box_type) const
1975 {
1976 const std::vector<Box_ipma::PropertyAssociation>* property_assoc = ipma->get_properties_for_item_ID(itemID);
1977 if (property_assoc == nullptr) {
1978 return nullptr;
1979 }
1980
1981 auto allProperties = get_all_child_boxes();
1982 for (const Box_ipma::PropertyAssociation& assoc : *property_assoc) {
1983 if (assoc.property_index > allProperties.size() ||
1984 assoc.property_index == 0) {
1985 return nullptr;
1986 }
1987
1988 auto property = allProperties[assoc.property_index - 1];
1989 if (property->get_short_type() == box_type) {
1990 return property;
1991 }
1992 }
1993
1994 return nullptr;
1995 }
1996
1997
parse(BitstreamRange & range)1998 Error Box_ispe::parse(BitstreamRange& range)
1999 {
2000 parse_full_box_header(range);
2001
2002 m_image_width = range.read32();
2003 m_image_height = range.read32();
2004
2005 return range.get_error();
2006 }
2007
2008
dump(Indent & indent) const2009 std::string Box_ispe::dump(Indent& indent) const
2010 {
2011 std::ostringstream sstr;
2012 sstr << Box::dump(indent);
2013
2014 sstr << indent << "image width: " << m_image_width << "\n"
2015 << indent << "image height: " << m_image_height << "\n";
2016
2017 return sstr.str();
2018 }
2019
2020
write(StreamWriter & writer) const2021 Error Box_ispe::write(StreamWriter& writer) const
2022 {
2023 size_t box_start = reserve_box_header_space(writer);
2024
2025 writer.write32(m_image_width);
2026 writer.write32(m_image_height);
2027
2028 prepend_header(writer, box_start);
2029
2030 return Error::Ok;
2031 }
2032
2033
parse(BitstreamRange & range)2034 Error Box_ipma::parse(BitstreamRange& range)
2035 {
2036 parse_full_box_header(range);
2037
2038 int entry_cnt = range.read32();
2039 for (int i = 0; i < entry_cnt && !range.error() && !range.eof(); i++) {
2040 Entry entry;
2041 if (get_version() < 1) {
2042 entry.item_ID = range.read16();
2043 }
2044 else {
2045 entry.item_ID = range.read32();
2046 }
2047
2048 int assoc_cnt = range.read8();
2049 for (int k = 0; k < assoc_cnt; k++) {
2050 PropertyAssociation association;
2051
2052 uint16_t index;
2053 if (get_flags() & 1) {
2054 index = range.read16();
2055 association.essential = !!(index & 0x8000);
2056 association.property_index = (index & 0x7fff);
2057 }
2058 else {
2059 index = range.read8();
2060 association.essential = !!(index & 0x80);
2061 association.property_index = (index & 0x7f);
2062 }
2063
2064 entry.associations.push_back(association);
2065 }
2066
2067 m_entries.push_back(entry);
2068 }
2069
2070 return range.get_error();
2071 }
2072
2073
get_properties_for_item_ID(uint32_t itemID) const2074 const std::vector<Box_ipma::PropertyAssociation>* Box_ipma::get_properties_for_item_ID(uint32_t itemID) const
2075 {
2076 for (const auto& entry : m_entries) {
2077 if (entry.item_ID == itemID) {
2078 return &entry.associations;
2079 }
2080 }
2081
2082 return nullptr;
2083 }
2084
2085
add_property_for_item_ID(heif_item_id itemID,PropertyAssociation assoc)2086 void Box_ipma::add_property_for_item_ID(heif_item_id itemID,
2087 PropertyAssociation assoc)
2088 {
2089 size_t idx;
2090 for (idx = 0; idx < m_entries.size(); idx++) {
2091 if (m_entries[idx].item_ID == itemID) {
2092 break;
2093 }
2094 }
2095
2096 // if itemID does not exist, add a new entry
2097 if (idx == m_entries.size()) {
2098 Entry entry;
2099 entry.item_ID = itemID;
2100 m_entries.push_back(entry);
2101 }
2102
2103 // add the property association
2104 m_entries[idx].associations.push_back(assoc);
2105 }
2106
2107
dump(Indent & indent) const2108 std::string Box_ipma::dump(Indent& indent) const
2109 {
2110 std::ostringstream sstr;
2111 sstr << Box::dump(indent);
2112
2113 for (const Entry& entry : m_entries) {
2114 sstr << indent << "associations for item ID: " << entry.item_ID << "\n";
2115 indent++;
2116 for (const auto& assoc : entry.associations) {
2117 sstr << indent << "property index: " << assoc.property_index
2118 << " (essential: " << std::boolalpha << assoc.essential << ")\n";
2119 }
2120 indent--;
2121 }
2122
2123 return sstr.str();
2124 }
2125
2126
derive_box_version()2127 void Box_ipma::derive_box_version()
2128 {
2129 int version = 0;
2130 bool large_property_indices = false;
2131
2132 for (const Entry& entry : m_entries) {
2133 if (entry.item_ID > 0xFFFF) {
2134 version = 1;
2135 }
2136
2137 for (const auto& assoc : entry.associations) {
2138 if (assoc.property_index > 0x7F) {
2139 large_property_indices = true;
2140 }
2141 }
2142 }
2143
2144 set_version((uint8_t) version);
2145 set_flags(large_property_indices ? 1 : 0);
2146 }
2147
2148
write(StreamWriter & writer) const2149 Error Box_ipma::write(StreamWriter& writer) const
2150 {
2151 size_t box_start = reserve_box_header_space(writer);
2152
2153 size_t entry_cnt = m_entries.size();
2154 writer.write32((uint32_t) entry_cnt);
2155
2156 for (const Entry& entry : m_entries) {
2157
2158 if (get_version() < 1) {
2159 writer.write16((uint16_t) entry.item_ID);
2160 }
2161 else {
2162 writer.write32(entry.item_ID);
2163 }
2164
2165 size_t assoc_cnt = entry.associations.size();
2166 if (assoc_cnt > 0xFF) {
2167 // TODO: error, too many associations
2168 }
2169
2170 writer.write8((uint8_t) assoc_cnt);
2171
2172 for (const PropertyAssociation& association : entry.associations) {
2173
2174 if (get_flags() & 1) {
2175 writer.write16((uint16_t) ((association.essential ? 0x8000 : 0) |
2176 (association.property_index & 0x7FFF)));
2177 }
2178 else {
2179 writer.write8((uint8_t) ((association.essential ? 0x80 : 0) |
2180 (association.property_index & 0x7F)));
2181 }
2182 }
2183 }
2184
2185 prepend_header(writer, box_start);
2186
2187 return Error::Ok;
2188 }
2189
2190
parse(BitstreamRange & range)2191 Error Box_auxC::parse(BitstreamRange& range)
2192 {
2193 parse_full_box_header(range);
2194
2195 m_aux_type = range.read_string();
2196
2197 while (!range.eof()) {
2198 m_aux_subtypes.push_back(range.read8());
2199 }
2200
2201 return range.get_error();
2202 }
2203
2204
write(StreamWriter & writer) const2205 Error Box_auxC::write(StreamWriter& writer) const
2206 {
2207 size_t box_start = reserve_box_header_space(writer);
2208
2209 writer.write(m_aux_type);
2210
2211 for (uint8_t subtype : m_aux_subtypes) {
2212 writer.write8(subtype);
2213 }
2214
2215 prepend_header(writer, box_start);
2216
2217 return Error::Ok;
2218 }
2219
2220
dump(Indent & indent) const2221 std::string Box_auxC::dump(Indent& indent) const
2222 {
2223 std::ostringstream sstr;
2224 sstr << Box::dump(indent);
2225
2226 sstr << indent << "aux type: " << m_aux_type << "\n"
2227 << indent << "aux subtypes: ";
2228 for (uint8_t subtype : m_aux_subtypes) {
2229 sstr << std::hex << std::setw(2) << std::setfill('0') << ((int) subtype) << " ";
2230 }
2231
2232 sstr << "\n";
2233
2234 return sstr.str();
2235 }
2236
2237
parse(BitstreamRange & range)2238 Error Box_irot::parse(BitstreamRange& range)
2239 {
2240 //parse_full_box_header(range);
2241
2242 uint16_t rotation = range.read8();
2243 rotation &= 0x03;
2244
2245 m_rotation = rotation * 90;
2246
2247 return range.get_error();
2248 }
2249
2250
dump(Indent & indent) const2251 std::string Box_irot::dump(Indent& indent) const
2252 {
2253 std::ostringstream sstr;
2254 sstr << Box::dump(indent);
2255
2256 sstr << indent << "rotation: " << m_rotation << " degrees (CCW)\n";
2257
2258 return sstr.str();
2259 }
2260
2261
parse(BitstreamRange & range)2262 Error Box_imir::parse(BitstreamRange& range)
2263 {
2264 //parse_full_box_header(range);
2265
2266 uint16_t axis = range.read8();
2267 if (axis & 1) {
2268 m_axis = MirrorAxis::Horizontal;
2269 }
2270 else {
2271 m_axis = MirrorAxis::Vertical;
2272 }
2273
2274 return range.get_error();
2275 }
2276
2277
dump(Indent & indent) const2278 std::string Box_imir::dump(Indent& indent) const
2279 {
2280 std::ostringstream sstr;
2281 sstr << Box::dump(indent);
2282
2283 sstr << indent << "mirror axis: ";
2284 switch (m_axis) {
2285 case MirrorAxis::Vertical:
2286 sstr << "vertical\n";
2287 break;
2288 case MirrorAxis::Horizontal:
2289 sstr << "horizontal\n";
2290 break;
2291 }
2292
2293 return sstr.str();
2294 }
2295
2296
parse(BitstreamRange & range)2297 Error Box_clap::parse(BitstreamRange& range)
2298 {
2299 //parse_full_box_header(range);
2300
2301 int32_t clean_aperture_width_num = range.read32();
2302 int32_t clean_aperture_width_den = range.read32();
2303 int32_t clean_aperture_height_num = range.read32();
2304 int32_t clean_aperture_height_den = range.read32();
2305 int32_t horizontal_offset_num = range.read32();
2306 int32_t horizontal_offset_den = range.read32();
2307 int32_t vertical_offset_num = range.read32();
2308 int32_t vertical_offset_den = range.read32();
2309 m_clean_aperture_width = Fraction(clean_aperture_width_num,
2310 clean_aperture_width_den);
2311 m_clean_aperture_height = Fraction(clean_aperture_height_num,
2312 clean_aperture_height_den);
2313 m_horizontal_offset = Fraction(horizontal_offset_num, horizontal_offset_den);
2314 m_vertical_offset = Fraction(vertical_offset_num, vertical_offset_den);
2315 if (!m_clean_aperture_width.is_valid() || !m_clean_aperture_height.is_valid() ||
2316 !m_horizontal_offset.is_valid() || !m_vertical_offset.is_valid()) {
2317 return Error(heif_error_Invalid_input,
2318 heif_suberror_Invalid_fractional_number);
2319 }
2320
2321 return range.get_error();
2322 }
2323
2324
write(StreamWriter & writer) const2325 Error Box_clap::write(StreamWriter& writer) const
2326 {
2327 size_t box_start = reserve_box_header_space(writer);
2328
2329 writer.write32(m_clean_aperture_width.numerator);
2330 writer.write32(m_clean_aperture_width.denominator);
2331 writer.write32(m_clean_aperture_height.numerator);
2332 writer.write32(m_clean_aperture_height.denominator);
2333 writer.write32(m_horizontal_offset.numerator);
2334 writer.write32(m_horizontal_offset.denominator);
2335 writer.write32(m_vertical_offset.numerator);
2336 writer.write32(m_vertical_offset.denominator);
2337
2338 prepend_header(writer, box_start);
2339
2340 return Error::Ok;
2341 }
2342
2343
dump(Indent & indent) const2344 std::string Box_clap::dump(Indent& indent) const
2345 {
2346 std::ostringstream sstr;
2347 sstr << Box::dump(indent);
2348
2349 sstr << indent << "clean_aperture: " << m_clean_aperture_width.numerator
2350 << "/" << m_clean_aperture_width.denominator << " x "
2351 << m_clean_aperture_height.numerator << "/"
2352 << m_clean_aperture_height.denominator << "\n";
2353 sstr << indent << "offset: " << m_horizontal_offset.numerator << "/"
2354 << m_horizontal_offset.denominator << " ; "
2355 << m_vertical_offset.numerator << "/"
2356 << m_vertical_offset.denominator << "\n";
2357
2358 return sstr.str();
2359 }
2360
2361
left_rounded(int image_width) const2362 int Box_clap::left_rounded(int image_width) const
2363 {
2364 // pcX = horizOff + (width - 1)/2
2365 // pcX ± (cleanApertureWidth - 1)/2
2366
2367 // left = horizOff + (width-1)/2 - (clapWidth-1)/2
2368
2369 Fraction pcX = m_horizontal_offset + Fraction(image_width - 1, 2);
2370 Fraction left = pcX - (m_clean_aperture_width - 1) / 2;
2371
2372 return left.round_down();
2373 }
2374
right_rounded(int image_width) const2375 int Box_clap::right_rounded(int image_width) const
2376 {
2377 Fraction right = m_clean_aperture_width - 1 + left_rounded(image_width);
2378
2379 return right.round();
2380 }
2381
top_rounded(int image_height) const2382 int Box_clap::top_rounded(int image_height) const
2383 {
2384 Fraction pcY = m_vertical_offset + Fraction(image_height - 1, 2);
2385 Fraction top = pcY - (m_clean_aperture_height - 1) / 2;
2386
2387 return top.round();
2388 }
2389
bottom_rounded(int image_height) const2390 int Box_clap::bottom_rounded(int image_height) const
2391 {
2392 Fraction bottom = m_clean_aperture_height - 1 + top_rounded(image_height);
2393
2394 return bottom.round();
2395 }
2396
get_width_rounded() const2397 int Box_clap::get_width_rounded() const
2398 {
2399 int left = (Fraction(0, 1) - (m_clean_aperture_width - 1) / 2).round();
2400 int right = ((m_clean_aperture_width - 1) / 2).round();
2401
2402 return right + 1 - left;
2403 }
2404
get_height_rounded() const2405 int Box_clap::get_height_rounded() const
2406 {
2407 int top = (Fraction(0, 1) - (m_clean_aperture_height - 1) / 2).round();
2408 int bottom = ((m_clean_aperture_height - 1) / 2).round();
2409
2410 return bottom + 1 - top;
2411 }
2412
set(uint32_t clap_width,uint32_t clap_height,uint32_t image_width,uint32_t image_height)2413 void Box_clap::set(uint32_t clap_width, uint32_t clap_height,
2414 uint32_t image_width, uint32_t image_height)
2415 {
2416 assert(image_width >= clap_width);
2417 assert(image_height >= clap_height);
2418
2419 m_clean_aperture_width = Fraction(clap_width, 1);
2420 m_clean_aperture_height = Fraction(clap_height, 1);
2421
2422 m_horizontal_offset = Fraction(-(int32_t) (image_width - clap_width), 2);
2423 m_vertical_offset = Fraction(-(int32_t) (image_height - clap_height), 2);
2424 }
2425
2426
parse(BitstreamRange & range)2427 Error Box_iref::parse(BitstreamRange& range)
2428 {
2429 parse_full_box_header(range);
2430
2431 while (!range.eof()) {
2432 Reference ref;
2433
2434 Error err = ref.header.parse(range);
2435 if (err != Error::Ok) {
2436 return err;
2437 }
2438
2439 if (get_version() == 0) {
2440 ref.from_item_ID = range.read16();
2441 int nRefs = range.read16();
2442 for (int i = 0; i < nRefs; i++) {
2443 ref.to_item_ID.push_back(range.read16());
2444 if (range.eof()) {
2445 break;
2446 }
2447 }
2448 }
2449 else {
2450 ref.from_item_ID = range.read32();
2451 int nRefs = range.read16();
2452 for (int i = 0; i < nRefs; i++) {
2453 ref.to_item_ID.push_back(range.read32());
2454 if (range.eof()) {
2455 break;
2456 }
2457 }
2458 }
2459
2460 m_references.push_back(ref);
2461 }
2462
2463 return range.get_error();
2464 }
2465
2466
derive_box_version()2467 void Box_iref::derive_box_version()
2468 {
2469 uint8_t version = 0;
2470
2471 for (const auto& ref : m_references) {
2472 if (ref.from_item_ID > 0xFFFF) {
2473 version = 1;
2474 break;
2475 }
2476
2477 for (uint32_t r : ref.to_item_ID) {
2478 if (r > 0xFFFF) {
2479 version = 1;
2480 break;
2481 }
2482 }
2483 }
2484
2485 set_version(version);
2486 }
2487
2488
write(StreamWriter & writer) const2489 Error Box_iref::write(StreamWriter& writer) const
2490 {
2491 size_t box_start = reserve_box_header_space(writer);
2492
2493 int id_size = ((get_version() == 0) ? 2 : 4);
2494
2495 for (const auto& ref : m_references) {
2496 uint32_t box_size = uint32_t(4 + 4 + 2 + id_size * (1 + ref.to_item_ID.size()));
2497
2498 // we write the BoxHeader ourselves since it is very simple
2499 writer.write32(box_size);
2500 writer.write32(ref.header.get_short_type());
2501
2502 writer.write(id_size, ref.from_item_ID);
2503 writer.write16((uint16_t) ref.to_item_ID.size());
2504
2505 for (uint32_t r : ref.to_item_ID) {
2506 writer.write(id_size, r);
2507 }
2508 }
2509
2510
2511 prepend_header(writer, box_start);
2512
2513 return Error::Ok;
2514 }
2515
2516
dump(Indent & indent) const2517 std::string Box_iref::dump(Indent& indent) const
2518 {
2519 std::ostringstream sstr;
2520 sstr << Box::dump(indent);
2521
2522 for (const auto& ref : m_references) {
2523 sstr << indent << "reference with type '" << ref.header.get_type_string() << "'"
2524 << " from ID: " << ref.from_item_ID
2525 << " to IDs: ";
2526 for (uint32_t id : ref.to_item_ID) {
2527 sstr << id << " ";
2528 }
2529 sstr << "\n";
2530 }
2531
2532 return sstr.str();
2533 }
2534
2535
has_references(uint32_t itemID) const2536 bool Box_iref::has_references(uint32_t itemID) const
2537 {
2538 for (const Reference& ref : m_references) {
2539 if (ref.from_item_ID == itemID) {
2540 return true;
2541 }
2542 }
2543
2544 return false;
2545 }
2546
2547
get_references_from(heif_item_id itemID) const2548 std::vector<Box_iref::Reference> Box_iref::get_references_from(heif_item_id itemID) const
2549 {
2550 std::vector<Reference> references;
2551
2552 for (const Reference& ref : m_references) {
2553 if (ref.from_item_ID == itemID) {
2554 references.push_back(ref);
2555 }
2556 }
2557
2558 return references;
2559 }
2560
2561
get_references(uint32_t itemID,uint32_t ref_type) const2562 std::vector<uint32_t> Box_iref::get_references(uint32_t itemID, uint32_t ref_type) const
2563 {
2564 for (const Reference& ref : m_references) {
2565 if (ref.from_item_ID == itemID &&
2566 ref.header.get_short_type() == ref_type) {
2567 return ref.to_item_ID;
2568 }
2569 }
2570
2571 return std::vector<uint32_t>();
2572 }
2573
2574
add_reference(heif_item_id from_id,uint32_t type,std::vector<heif_item_id> to_ids)2575 void Box_iref::add_reference(heif_item_id from_id, uint32_t type, std::vector<heif_item_id> to_ids)
2576 {
2577 Reference ref;
2578 ref.header.set_short_type(type);
2579 ref.from_item_ID = from_id;
2580 ref.to_item_ID = to_ids;
2581
2582 m_references.push_back(ref);
2583 }
2584
2585
parse(BitstreamRange & range)2586 Error Box_hvcC::parse(BitstreamRange& range)
2587 {
2588 //parse_full_box_header(range);
2589
2590 uint8_t byte;
2591
2592 auto& c = m_configuration; // abbreviation
2593
2594 c.configuration_version = range.read8();
2595 byte = range.read8();
2596 c.general_profile_space = (byte >> 6) & 3;
2597 c.general_tier_flag = (byte >> 5) & 1;
2598 c.general_profile_idc = (byte & 0x1F);
2599
2600 c.general_profile_compatibility_flags = range.read32();
2601
2602 for (int i = 0; i < 6; i++) {
2603 byte = range.read8();
2604
2605 for (int b = 0; b < 8; b++) {
2606 c.general_constraint_indicator_flags[i * 8 + b] = (byte >> (7 - b)) & 1;
2607 }
2608 }
2609
2610 c.general_level_idc = range.read8();
2611 c.min_spatial_segmentation_idc = range.read16() & 0x0FFF;
2612 c.parallelism_type = range.read8() & 0x03;
2613 c.chroma_format = range.read8() & 0x03;
2614 c.bit_depth_luma = static_cast<uint8_t>((range.read8() & 0x07) + 8);
2615 c.bit_depth_chroma = static_cast<uint8_t>((range.read8() & 0x07) + 8);
2616 c.avg_frame_rate = range.read16();
2617
2618 byte = range.read8();
2619 c.constant_frame_rate = (byte >> 6) & 0x03;
2620 c.num_temporal_layers = (byte >> 3) & 0x07;
2621 c.temporal_id_nested = (byte >> 2) & 1;
2622
2623 m_length_size = static_cast<uint8_t>((byte & 0x03) + 1);
2624
2625 int nArrays = range.read8();
2626
2627 for (int i = 0; i < nArrays && !range.error(); i++) {
2628 byte = range.read8();
2629
2630 NalArray array;
2631
2632 array.m_array_completeness = (byte >> 6) & 1;
2633 array.m_NAL_unit_type = (byte & 0x3F);
2634
2635 int nUnits = range.read16();
2636 for (int u = 0; u < nUnits && !range.error(); u++) {
2637
2638 std::vector<uint8_t> nal_unit;
2639 int size = range.read16();
2640 if (!size) {
2641 // Ignore empty NAL units.
2642 continue;
2643 }
2644
2645 if (range.prepare_read(size)) {
2646 nal_unit.resize(size);
2647 range.get_istream()->read((char*) nal_unit.data(), size);
2648 }
2649
2650 array.m_nal_units.push_back(std::move(nal_unit));
2651 }
2652
2653 m_nal_array.push_back(std::move(array));
2654 }
2655
2656 range.skip_to_end_of_box();
2657
2658 return range.get_error();
2659 }
2660
2661
dump(Indent & indent) const2662 std::string Box_hvcC::dump(Indent& indent) const
2663 {
2664 std::ostringstream sstr;
2665 sstr << Box::dump(indent);
2666
2667 const auto& c = m_configuration; // abbreviation
2668
2669 sstr << indent << "configuration_version: " << ((int) c.configuration_version) << "\n"
2670 << indent << "general_profile_space: " << ((int) c.general_profile_space) << "\n"
2671 << indent << "general_tier_flag: " << c.general_tier_flag << "\n"
2672 << indent << "general_profile_idc: " << ((int) c.general_profile_idc) << "\n";
2673
2674 sstr << indent << "general_profile_compatibility_flags: ";
2675 for (int i = 0; i < 32; i++) {
2676 sstr << ((c.general_profile_compatibility_flags >> (31 - i)) & 1);
2677 if ((i % 8) == 7) sstr << ' ';
2678 else if ((i % 4) == 3) sstr << '.';
2679 }
2680 sstr << "\n";
2681
2682 sstr << indent << "general_constraint_indicator_flags: ";
2683 int cnt = 0;
2684 for (int i = 0; i < configuration::NUM_CONSTRAINT_INDICATOR_FLAGS; i++) {
2685 bool b = c.general_constraint_indicator_flags[i];
2686
2687 sstr << (b ? 1 : 0);
2688 cnt++;
2689 if ((cnt % 8) == 0)
2690 sstr << ' ';
2691 }
2692 sstr << "\n";
2693
2694 sstr << indent << "general_level_idc: " << ((int) c.general_level_idc) << "\n"
2695 << indent << "min_spatial_segmentation_idc: " << c.min_spatial_segmentation_idc << "\n"
2696 << indent << "parallelism_type: " << ((int) c.parallelism_type) << "\n"
2697 << indent << "chroma_format: " << ((int) c.chroma_format) << "\n"
2698 << indent << "bit_depth_luma: " << ((int) c.bit_depth_luma) << "\n"
2699 << indent << "bit_depth_chroma: " << ((int) c.bit_depth_chroma) << "\n"
2700 << indent << "avg_frame_rate: " << c.avg_frame_rate << "\n"
2701 << indent << "constant_frame_rate: " << ((int) c.constant_frame_rate) << "\n"
2702 << indent << "num_temporal_layers: " << ((int) c.num_temporal_layers) << "\n"
2703 << indent << "temporal_id_nested: " << ((int) c.temporal_id_nested) << "\n"
2704 << indent << "length_size: " << ((int) m_length_size) << "\n";
2705
2706 for (const auto& array : m_nal_array) {
2707 sstr << indent << "<array>\n";
2708
2709 indent++;
2710 sstr << indent << "array_completeness: " << ((int) array.m_array_completeness) << "\n"
2711 << indent << "NAL_unit_type: " << ((int) array.m_NAL_unit_type) << "\n";
2712
2713 for (const auto& unit : array.m_nal_units) {
2714 //sstr << " unit with " << unit.size() << " bytes of data\n";
2715 sstr << indent;
2716 for (uint8_t b : unit) {
2717 sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " ";
2718 }
2719 sstr << "\n";
2720 sstr << std::dec;
2721 }
2722
2723 indent--;
2724 }
2725
2726 return sstr.str();
2727 }
2728
2729
get_headers(std::vector<uint8_t> * dest) const2730 bool Box_hvcC::get_headers(std::vector<uint8_t>* dest) const
2731 {
2732 for (const auto& array : m_nal_array) {
2733 for (const auto& unit : array.m_nal_units) {
2734
2735 dest->push_back((unit.size() >> 24) & 0xFF);
2736 dest->push_back((unit.size() >> 16) & 0xFF);
2737 dest->push_back((unit.size() >> 8) & 0xFF);
2738 dest->push_back((unit.size() >> 0) & 0xFF);
2739
2740 /*
2741 dest->push_back(0);
2742 dest->push_back(0);
2743 dest->push_back(1);
2744 */
2745
2746 dest->insert(dest->end(), unit.begin(), unit.end());
2747 }
2748 }
2749
2750 return true;
2751 }
2752
2753
append_nal_data(const std::vector<uint8_t> & nal)2754 void Box_hvcC::append_nal_data(const std::vector<uint8_t>& nal)
2755 {
2756 NalArray array;
2757 array.m_array_completeness = 0;
2758 array.m_NAL_unit_type = uint8_t(nal[0] >> 1);
2759 array.m_nal_units.push_back(nal);
2760
2761 m_nal_array.push_back(array);
2762 }
2763
append_nal_data(const uint8_t * data,size_t size)2764 void Box_hvcC::append_nal_data(const uint8_t* data, size_t size)
2765 {
2766 std::vector<uint8_t> nal;
2767 nal.resize(size);
2768 memcpy(nal.data(), data, size);
2769
2770 NalArray array;
2771 array.m_array_completeness = 0;
2772 array.m_NAL_unit_type = uint8_t(nal[0] >> 1);
2773 array.m_nal_units.push_back(std::move(nal));
2774
2775 m_nal_array.push_back(array);
2776 }
2777
2778
write(StreamWriter & writer) const2779 Error Box_hvcC::write(StreamWriter& writer) const
2780 {
2781 size_t box_start = reserve_box_header_space(writer);
2782
2783 const auto& c = m_configuration; // abbreviation
2784
2785 writer.write8(c.configuration_version);
2786
2787 writer.write8((uint8_t) (((c.general_profile_space & 3) << 6) |
2788 ((c.general_tier_flag & 1) << 5) |
2789 (c.general_profile_idc & 0x1F)));
2790
2791 writer.write32(c.general_profile_compatibility_flags);
2792
2793 for (int i = 0; i < 6; i++) {
2794 uint8_t byte = 0;
2795
2796 for (int b = 0; b < 8; b++) {
2797 if (c.general_constraint_indicator_flags[i * 8 + b]) {
2798 byte |= 1;
2799 }
2800
2801 byte = (uint8_t) (byte << 1);
2802 }
2803
2804 writer.write8(byte);
2805 }
2806
2807 writer.write8(c.general_level_idc);
2808 writer.write16((c.min_spatial_segmentation_idc & 0x0FFF) | 0xF000);
2809 writer.write8(c.parallelism_type | 0xFC);
2810 writer.write8(c.chroma_format | 0xFC);
2811 writer.write8((uint8_t) ((c.bit_depth_luma - 8) | 0xF8));
2812 writer.write8((uint8_t) ((c.bit_depth_chroma - 8) | 0xF8));
2813 writer.write16(c.avg_frame_rate);
2814
2815 writer.write8((uint8_t) (((c.constant_frame_rate & 0x03) << 6) |
2816 ((c.num_temporal_layers & 0x07) << 3) |
2817 ((c.temporal_id_nested & 1) << 2) |
2818 ((m_length_size - 1) & 0x03)));
2819
2820 size_t nArrays = m_nal_array.size();
2821 if (nArrays > 0xFF) {
2822 // TODO: error: too many NAL units
2823 }
2824
2825 writer.write8((uint8_t) nArrays);
2826
2827 for (const NalArray& array : m_nal_array) {
2828
2829 writer.write8((uint8_t) (((array.m_array_completeness & 1) << 6) |
2830 (array.m_NAL_unit_type & 0x3F)));
2831
2832 size_t nUnits = array.m_nal_units.size();
2833 if (nUnits > 0xFFFF) {
2834 // TODO: error: too many NAL units
2835 }
2836
2837 writer.write16((uint16_t) nUnits);
2838
2839 for (const std::vector<uint8_t>& nal_unit : array.m_nal_units) {
2840 writer.write16((uint16_t) nal_unit.size());
2841 writer.write(nal_unit);
2842 }
2843 }
2844
2845 prepend_header(writer, box_start);
2846
2847 return Error::Ok;
2848 }
2849
2850
parse(BitstreamRange & range)2851 Error Box_av1C::parse(BitstreamRange& range)
2852 {
2853 //parse_full_box_header(range);
2854
2855 uint8_t byte;
2856
2857 auto& c = m_configuration; // abbreviation
2858
2859 byte = range.read8();
2860 if ((byte & 0x80) == 0) {
2861 // error: marker bit not set
2862 }
2863
2864 c.version = byte & 0x7F;
2865
2866 byte = range.read8();
2867 c.seq_profile = (byte >> 5) & 0x7;
2868 c.seq_level_idx_0 = byte & 0x1f;
2869
2870 byte = range.read8();
2871 c.seq_tier_0 = (byte >> 7) & 1;
2872 c.high_bitdepth = (byte >> 6) & 1;
2873 c.twelve_bit = (byte >> 5) & 1;
2874 c.monochrome = (byte >> 4) & 1;
2875 c.chroma_subsampling_x = (byte >> 3) & 1;
2876 c.chroma_subsampling_y = (byte >> 2) & 1;
2877 c.chroma_sample_position = byte & 3;
2878
2879 byte = range.read8();
2880 c.initial_presentation_delay_present = (byte >> 4) & 1;
2881 if (c.initial_presentation_delay_present) {
2882 c.initial_presentation_delay_minus_one = byte & 0x0F;
2883 }
2884
2885 const int64_t configOBUs_bytes = range.get_remaining_bytes();
2886 m_config_OBUs.resize(configOBUs_bytes);
2887
2888 if (!range.read(m_config_OBUs.data(), configOBUs_bytes)) {
2889 // error
2890 }
2891
2892 return range.get_error();
2893 }
2894
2895
write(StreamWriter & writer) const2896 Error Box_av1C::write(StreamWriter& writer) const
2897 {
2898 size_t box_start = reserve_box_header_space(writer);
2899
2900 const auto& c = m_configuration; // abbreviation
2901
2902 writer.write8(c.version | 0x80);
2903
2904 writer.write8((uint8_t) (((c.seq_profile & 0x7) << 5) |
2905 (c.seq_level_idx_0 & 0x1f)));
2906
2907 writer.write8((uint8_t) ((c.seq_tier_0 ? 0x80 : 0) |
2908 (c.high_bitdepth ? 0x40 : 0) |
2909 (c.twelve_bit ? 0x20 : 0) |
2910 (c.monochrome ? 0x10 : 0) |
2911 (c.chroma_subsampling_x ? 0x08 : 0) |
2912 (c.chroma_subsampling_y ? 0x04 : 0) |
2913 (c.chroma_sample_position & 0x03)));
2914
2915 writer.write8(0); // TODO initial_presentation_delay
2916
2917 prepend_header(writer, box_start);
2918
2919 return Error::Ok;
2920 }
2921
2922
dump(Indent & indent) const2923 std::string Box_av1C::dump(Indent& indent) const
2924 {
2925 std::ostringstream sstr;
2926 sstr << Box::dump(indent);
2927
2928 const auto& c = m_configuration; // abbreviation
2929
2930 sstr << indent << "version: " << ((int) c.version) << "\n"
2931 << indent << "seq_profile: " << ((int) c.seq_profile) << "\n"
2932 << indent << "seq_level_idx_0: " << ((int) c.seq_level_idx_0) << "\n"
2933 << indent << "high_bitdepth: " << ((int) c.high_bitdepth) << "\n"
2934 << indent << "twelve_bit: " << ((int) c.twelve_bit) << "\n"
2935 << indent << "chroma_subsampling_x: " << ((int) c.chroma_subsampling_x) << "\n"
2936 << indent << "chroma_subsampling_y: " << ((int) c.chroma_subsampling_y) << "\n"
2937 << indent << "chroma_sample_position: " << ((int) c.chroma_sample_position) << "\n"
2938 << indent << "initial_presentation_delay: ";
2939
2940 if (c.initial_presentation_delay_present) {
2941 sstr << c.initial_presentation_delay_minus_one + 1 << "\n";
2942 }
2943 else {
2944 sstr << "not present\n";
2945 }
2946
2947 sstr << indent << "config OBUs:";
2948 for (size_t i = 0; i < m_config_OBUs.size(); i++) {
2949 sstr << " " << std::hex << std::setfill('0') << std::setw(2)
2950 << ((int) m_config_OBUs[i]);
2951 }
2952 sstr << std::dec << "\n";
2953
2954 return sstr.str();
2955 }
2956
2957
parse(BitstreamRange & range)2958 Error Box_idat::parse(BitstreamRange& range)
2959 {
2960 //parse_full_box_header(range);
2961
2962 m_data_start_pos = range.get_istream()->get_position();
2963
2964 return range.get_error();
2965 }
2966
2967
write(StreamWriter & writer) const2968 Error Box_idat::write(StreamWriter& writer) const
2969 {
2970 size_t box_start = reserve_box_header_space(writer);
2971
2972 writer.write(m_data_for_writing);
2973
2974 prepend_header(writer, box_start);
2975
2976 return Error::Ok;
2977 }
2978
2979
dump(Indent & indent) const2980 std::string Box_idat::dump(Indent& indent) const
2981 {
2982 std::ostringstream sstr;
2983 sstr << Box::dump(indent);
2984
2985 sstr << indent << "number of data bytes: " << get_box_size() - get_header_size() << "\n";
2986
2987 return sstr.str();
2988 }
2989
2990
read_data(std::shared_ptr<StreamReader> istr,uint64_t start,uint64_t length,std::vector<uint8_t> & out_data) const2991 Error Box_idat::read_data(std::shared_ptr<StreamReader> istr,
2992 uint64_t start, uint64_t length,
2993 std::vector<uint8_t>& out_data) const
2994 {
2995 // --- security check that we do not allocate too much data
2996
2997 auto curr_size = out_data.size();
2998
2999 if (MAX_MEMORY_BLOCK_SIZE - curr_size < length) {
3000 std::stringstream sstr;
3001 sstr << "idat box contained " << length << " bytes, total memory size would be "
3002 << (curr_size + length) << " bytes, exceeding the security limit of "
3003 << MAX_MEMORY_BLOCK_SIZE << " bytes";
3004
3005 return Error(heif_error_Memory_allocation_error,
3006 heif_suberror_Security_limit_exceeded,
3007 sstr.str());
3008 }
3009
3010
3011 // move to start of data
3012 if (start > (uint64_t) m_data_start_pos + get_box_size()) {
3013 return Error(heif_error_Invalid_input,
3014 heif_suberror_End_of_data);
3015 }
3016 else if (length > get_box_size() || start + length > get_box_size()) {
3017 return Error(heif_error_Invalid_input,
3018 heif_suberror_End_of_data);
3019 }
3020
3021 StreamReader::grow_status status = istr->wait_for_file_size((int64_t) m_data_start_pos + start + length);
3022 if (status == StreamReader::size_beyond_eof ||
3023 status == StreamReader::timeout) {
3024 // TODO: maybe we should introduce some 'Recoverable error' instead of 'Invalid input'
3025 return Error(heif_error_Invalid_input,
3026 heif_suberror_End_of_data);
3027 }
3028
3029 bool success;
3030 success = istr->seek(m_data_start_pos + (std::streampos) start);
3031 assert(success);
3032 (void) success;
3033
3034 if (length > 0) {
3035 // reserve space for the data in the output array
3036 out_data.resize(static_cast<size_t>(curr_size + length));
3037 uint8_t* data = &out_data[curr_size];
3038
3039 success = istr->read((char*) data, static_cast<size_t>(length));
3040 assert(success);
3041 }
3042
3043 return Error::Ok;
3044 }
3045
3046
parse(BitstreamRange & range)3047 Error Box_grpl::parse(BitstreamRange& range)
3048 {
3049 //parse_full_box_header(range);
3050
3051 //return read_children(range);
3052
3053 while (!range.eof()) {
3054 EntityGroup group;
3055 Error err = group.header.parse(range);
3056 if (err != Error::Ok) {
3057 return err;
3058 }
3059
3060 err = group.header.parse_full_box_header(range);
3061 if (err != Error::Ok) {
3062 return err;
3063 }
3064
3065 group.group_id = range.read32();
3066 int nEntities = range.read32();
3067 for (int i = 0; i < nEntities; i++) {
3068 if (range.eof()) {
3069 break;
3070 }
3071
3072 group.entity_ids.push_back(range.read32());
3073 }
3074
3075 m_entity_groups.push_back(group);
3076 }
3077
3078 return range.get_error();
3079 }
3080
3081
dump(Indent & indent) const3082 std::string Box_grpl::dump(Indent& indent) const
3083 {
3084 std::ostringstream sstr;
3085 sstr << Box::dump(indent);
3086
3087 for (const auto& group : m_entity_groups) {
3088 sstr << indent << "group type: " << group.header.get_type_string() << "\n"
3089 << indent << "| group id: " << group.group_id << "\n"
3090 << indent << "| entity IDs: ";
3091
3092 for (uint32_t id : group.entity_ids) {
3093 sstr << id << " ";
3094 }
3095
3096 sstr << "\n";
3097 }
3098
3099 return sstr.str();
3100 }
3101
3102
parse(BitstreamRange & range)3103 Error Box_dinf::parse(BitstreamRange& range)
3104 {
3105 //parse_full_box_header(range);
3106
3107 return read_children(range);
3108 }
3109
3110
dump(Indent & indent) const3111 std::string Box_dinf::dump(Indent& indent) const
3112 {
3113 std::ostringstream sstr;
3114 sstr << Box::dump(indent);
3115 sstr << dump_children(indent);
3116
3117 return sstr.str();
3118 }
3119
3120
parse(BitstreamRange & range)3121 Error Box_dref::parse(BitstreamRange& range)
3122 {
3123 parse_full_box_header(range);
3124
3125 int nEntities = range.read32();
3126
3127 /*
3128 for (int i=0;i<nEntities;i++) {
3129 if (range.eof()) {
3130 break;
3131 }
3132 }
3133 */
3134
3135 Error err = read_children(range, nEntities);
3136 if (err) {
3137 return err;
3138 }
3139
3140 if ((int) m_children.size() != nEntities) {
3141 // TODO return Error(
3142 }
3143
3144 return err;
3145 }
3146
3147
dump(Indent & indent) const3148 std::string Box_dref::dump(Indent& indent) const
3149 {
3150 std::ostringstream sstr;
3151 sstr << Box::dump(indent);
3152 sstr << dump_children(indent);
3153
3154 return sstr.str();
3155 }
3156
3157
parse(BitstreamRange & range)3158 Error Box_url::parse(BitstreamRange& range)
3159 {
3160 parse_full_box_header(range);
3161
3162 m_location = range.read_string();
3163
3164 return range.get_error();
3165 }
3166
3167
dump(Indent & indent) const3168 std::string Box_url::dump(Indent& indent) const
3169 {
3170 std::ostringstream sstr;
3171 sstr << Box::dump(indent);
3172 //sstr << dump_children(indent);
3173
3174 sstr << indent << "location: " << m_location << "\n";
3175
3176 return sstr.str();
3177 }
3178