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