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
21 #include "heif_file.h"
22
23 #include <fstream>
24 #include <limits>
25 #include <sstream>
26 #include <utility>
27 #include <cstring>
28 #include <cassert>
29
30 #ifdef _MSC_VER
31
32 #ifndef NOMINMAX
33 #define NOMINMAX 1
34 #endif
35
36 #include <Windows.h>
37 #endif
38
39 using namespace heif;
40
41 // TODO: make this a decoder option
42 #define STRICT_PARSING false
43
44
HeifFile()45 HeifFile::HeifFile()
46 {
47 }
48
49
~HeifFile()50 HeifFile::~HeifFile()
51 {
52 }
53
54
get_item_IDs() const55 std::vector<heif_item_id> HeifFile::get_item_IDs() const
56 {
57 std::vector<heif_item_id> IDs;
58
59 for (const auto& infe : m_infe_boxes) {
60 IDs.push_back(infe.second->get_item_ID());
61 }
62
63 return IDs;
64 }
65
66
read_from_file(const char * input_filename)67 Error HeifFile::read_from_file(const char* input_filename)
68 {
69 #ifdef _MSC_VER
70 auto input_stream_istr = std::unique_ptr<std::istream>(new std::ifstream(convert_utf8_path_to_utf16(input_filename).c_str(), std::ios_base::binary));
71 #else
72 auto input_stream_istr = std::unique_ptr<std::istream>(new std::ifstream(input_filename, std::ios_base::binary));
73 #endif
74 if (!input_stream_istr->good()) {
75 std::stringstream sstr;
76 sstr << "Error opening file: " << strerror(errno) << " (" << errno << ")\n";
77 return Error(heif_error_Input_does_not_exist, heif_suberror_Unspecified, sstr.str());
78 }
79
80 auto input_stream = std::make_shared<StreamReader_istream>(std::move(input_stream_istr));
81 return read(input_stream);
82 }
83
84
read_from_memory(const void * data,size_t size,bool copy)85 Error HeifFile::read_from_memory(const void* data, size_t size, bool copy)
86 {
87 auto input_stream = std::make_shared<StreamReader_memory>((const uint8_t*) data, size, copy);
88
89 return read(input_stream);
90 }
91
92
read(std::shared_ptr<StreamReader> reader)93 Error HeifFile::read(std::shared_ptr<StreamReader> reader)
94 {
95 m_input_stream = reader;
96
97 uint64_t maxSize = std::numeric_limits<int64_t>::max();
98 heif::BitstreamRange range(m_input_stream, maxSize);
99
100 Error error = parse_heif_file(range);
101 return error;
102 }
103
104
new_empty_file()105 void HeifFile::new_empty_file()
106 {
107 m_input_stream.reset();
108 m_top_level_boxes.clear();
109
110 m_ftyp_box = std::make_shared<Box_ftyp>();
111 m_hdlr_box = std::make_shared<Box_hdlr>();
112 m_meta_box = std::make_shared<Box_meta>();
113 m_ipco_box = std::make_shared<Box_ipco>();
114 m_ipma_box = std::make_shared<Box_ipma>();
115 m_iloc_box = std::make_shared<Box_iloc>();
116 m_iinf_box = std::make_shared<Box_iinf>();
117 m_iprp_box = std::make_shared<Box_iprp>();
118 m_pitm_box = std::make_shared<Box_pitm>();
119
120 m_meta_box->append_child_box(m_hdlr_box);
121 m_meta_box->append_child_box(m_pitm_box);
122 m_meta_box->append_child_box(m_iloc_box);
123 m_meta_box->append_child_box(m_iinf_box);
124 m_meta_box->append_child_box(m_iprp_box);
125
126 m_iprp_box->append_child_box(m_ipco_box);
127 m_iprp_box->append_child_box(m_ipma_box);
128
129 m_infe_boxes.clear();
130
131 m_top_level_boxes.push_back(m_ftyp_box);
132 m_top_level_boxes.push_back(m_meta_box);
133 }
134
135
set_brand(heif_compression_format format)136 void HeifFile::set_brand(heif_compression_format format)
137 {
138 switch (format) {
139 case heif_compression_HEVC:
140 m_ftyp_box->set_major_brand(fourcc("heic"));
141 m_ftyp_box->set_minor_version(0);
142 m_ftyp_box->add_compatible_brand(fourcc("mif1"));
143 m_ftyp_box->add_compatible_brand(fourcc("heic"));
144 break;
145
146 case heif_compression_AV1:
147 m_ftyp_box->set_major_brand(fourcc("avif"));
148 m_ftyp_box->set_minor_version(0);
149 m_ftyp_box->add_compatible_brand(fourcc("avif"));
150 m_ftyp_box->add_compatible_brand(fourcc("mif1"));
151 break;
152
153 default:
154 break;
155 }
156 }
157
158
write(StreamWriter & writer)159 void HeifFile::write(StreamWriter& writer)
160 {
161 for (auto& box : m_top_level_boxes) {
162 box->derive_box_version_recursive();
163 box->write(writer);
164 }
165
166 m_iloc_box->write_mdat_after_iloc(writer);
167 }
168
169
debug_dump_boxes() const170 std::string HeifFile::debug_dump_boxes() const
171 {
172 std::stringstream sstr;
173
174 bool first = true;
175
176 for (const auto& box : m_top_level_boxes) {
177 // dump box content for debugging
178
179 if (first) {
180 first = false;
181 }
182 else {
183 sstr << "\n";
184 }
185
186 heif::Indent indent;
187 sstr << box->dump(indent);
188 }
189
190 return sstr.str();
191 }
192
193
parse_heif_file(BitstreamRange & range)194 Error HeifFile::parse_heif_file(BitstreamRange& range)
195 {
196 // --- read all top-level boxes
197
198 for (;;) {
199 std::shared_ptr<Box> box;
200 Error error = Box::read(range, &box);
201
202 // When an EOF error is returned, this is not really a fatal exception,
203 // but simply the indication that we reached the end of the file.
204 if (error != Error::Ok || range.error() || range.eof()) {
205 break;
206 }
207
208 m_top_level_boxes.push_back(box);
209
210
211 // extract relevant boxes (ftyp, meta)
212
213 if (box->get_short_type() == fourcc("meta")) {
214 m_meta_box = std::dynamic_pointer_cast<Box_meta>(box);
215 }
216
217 if (box->get_short_type() == fourcc("ftyp")) {
218 m_ftyp_box = std::dynamic_pointer_cast<Box_ftyp>(box);
219 }
220 }
221
222
223
224 // --- check whether this is a HEIF file and its structural format
225
226 if (!m_ftyp_box) {
227 return Error(heif_error_Invalid_input,
228 heif_suberror_No_ftyp_box);
229 }
230
231 if (!m_ftyp_box->has_compatible_brand(fourcc("heic")) &&
232 !m_ftyp_box->has_compatible_brand(fourcc("heix")) &&
233 !m_ftyp_box->has_compatible_brand(fourcc("mif1")) &&
234 !m_ftyp_box->has_compatible_brand(fourcc("avif"))) {
235 std::stringstream sstr;
236 sstr << "File does not include any supported brands.\n";
237
238 return Error(heif_error_Unsupported_filetype,
239 heif_suberror_Unspecified,
240 sstr.str());
241 }
242
243 if (!m_meta_box) {
244 return Error(heif_error_Invalid_input,
245 heif_suberror_No_meta_box);
246 }
247
248
249 m_hdlr_box = std::dynamic_pointer_cast<Box_hdlr>(m_meta_box->get_child_box(fourcc("hdlr")));
250 if (STRICT_PARSING && !m_hdlr_box) {
251 return Error(heif_error_Invalid_input,
252 heif_suberror_No_hdlr_box);
253 }
254
255 if (m_hdlr_box &&
256 m_hdlr_box->get_handler_type() != fourcc("pict")) {
257 return Error(heif_error_Invalid_input,
258 heif_suberror_No_pict_handler);
259 }
260
261
262 // --- find mandatory boxes needed for image decoding
263
264 m_pitm_box = std::dynamic_pointer_cast<Box_pitm>(m_meta_box->get_child_box(fourcc("pitm")));
265 if (!m_pitm_box) {
266 return Error(heif_error_Invalid_input,
267 heif_suberror_No_pitm_box);
268 }
269
270 m_iprp_box = std::dynamic_pointer_cast<Box_iprp>(m_meta_box->get_child_box(fourcc("iprp")));
271 if (!m_iprp_box) {
272 return Error(heif_error_Invalid_input,
273 heif_suberror_No_iprp_box);
274 }
275
276 m_ipco_box = std::dynamic_pointer_cast<Box_ipco>(m_iprp_box->get_child_box(fourcc("ipco")));
277 if (!m_ipco_box) {
278 return Error(heif_error_Invalid_input,
279 heif_suberror_No_ipco_box);
280 }
281
282 m_ipma_box = std::dynamic_pointer_cast<Box_ipma>(m_iprp_box->get_child_box(fourcc("ipma")));
283 if (!m_ipma_box) {
284 return Error(heif_error_Invalid_input,
285 heif_suberror_No_ipma_box);
286 }
287
288 m_iloc_box = std::dynamic_pointer_cast<Box_iloc>(m_meta_box->get_child_box(fourcc("iloc")));
289 if (!m_iloc_box) {
290 return Error(heif_error_Invalid_input,
291 heif_suberror_No_iloc_box);
292 }
293
294 m_idat_box = std::dynamic_pointer_cast<Box_idat>(m_meta_box->get_child_box(fourcc("idat")));
295
296 m_iref_box = std::dynamic_pointer_cast<Box_iref>(m_meta_box->get_child_box(fourcc("iref")));
297
298 m_iinf_box = std::dynamic_pointer_cast<Box_iinf>(m_meta_box->get_child_box(fourcc("iinf")));
299 if (!m_iinf_box) {
300 return Error(heif_error_Invalid_input,
301 heif_suberror_No_iinf_box);
302 }
303
304
305
306 // --- build list of images
307
308 std::vector<std::shared_ptr<Box>> infe_boxes = m_iinf_box->get_child_boxes(fourcc("infe"));
309
310 for (auto& box : infe_boxes) {
311 std::shared_ptr<Box_infe> infe_box = std::dynamic_pointer_cast<Box_infe>(box);
312 if (!infe_box) {
313 return Error(heif_error_Invalid_input,
314 heif_suberror_No_infe_box);
315 }
316
317 m_infe_boxes.insert(std::make_pair(infe_box->get_item_ID(), infe_box));
318 }
319
320 return Error::Ok;
321 }
322
323
image_exists(heif_item_id ID) const324 bool HeifFile::image_exists(heif_item_id ID) const
325 {
326 auto image_iter = m_infe_boxes.find(ID);
327 return image_iter != m_infe_boxes.end();
328 }
329
330
get_infe(heif_item_id ID) const331 std::shared_ptr<Box_infe> HeifFile::get_infe(heif_item_id ID) const
332 {
333 // --- get the image from the list of all images
334
335 auto image_iter = m_infe_boxes.find(ID);
336 if (image_iter == m_infe_boxes.end()) {
337 return nullptr;
338 }
339
340 return image_iter->second;
341 }
342
343
get_item_type(heif_item_id ID) const344 std::string HeifFile::get_item_type(heif_item_id ID) const
345 {
346 auto infe_box = get_infe(ID);
347 if (!infe_box) {
348 return "";
349 }
350
351 return infe_box->get_item_type();
352 }
353
354
get_content_type(heif_item_id ID) const355 std::string HeifFile::get_content_type(heif_item_id ID) const
356 {
357 auto infe_box = get_infe(ID);
358 if (!infe_box) {
359 return "";
360 }
361
362 return infe_box->get_content_type();
363 }
364
365
get_properties(heif_item_id imageID,std::vector<Box_ipco::Property> & properties) const366 Error HeifFile::get_properties(heif_item_id imageID,
367 std::vector<Box_ipco::Property>& properties) const
368 {
369 if (!m_ipco_box) {
370 return Error(heif_error_Invalid_input,
371 heif_suberror_No_ipco_box);
372 }
373 else if (!m_ipma_box) {
374 return Error(heif_error_Invalid_input,
375 heif_suberror_No_ipma_box);
376 }
377
378 return m_ipco_box->get_properties_for_item_ID(imageID, m_ipma_box, properties);
379 }
380
381
get_image_chroma_from_configuration(heif_item_id imageID) const382 heif_chroma HeifFile::get_image_chroma_from_configuration(heif_item_id imageID) const
383 {
384 // HEVC
385
386 auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("hvcC"));
387 std::shared_ptr<Box_hvcC> hvcC_box = std::dynamic_pointer_cast<Box_hvcC>(box);
388 if (hvcC_box) {
389 return (heif_chroma) (hvcC_box->get_configuration().chroma_format);
390 }
391
392
393 // AV1
394
395 box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("av1C"));
396 std::shared_ptr<Box_av1C> av1C_box = std::dynamic_pointer_cast<Box_av1C>(box);
397 if (av1C_box) {
398 Box_av1C::configuration config = av1C_box->get_configuration();
399 if (config.chroma_subsampling_x == 1 &&
400 config.chroma_subsampling_y == 1) {
401 return heif_chroma_420;
402 }
403 else if (config.chroma_subsampling_x == 1 &&
404 config.chroma_subsampling_y == 0) {
405 return heif_chroma_422;
406 }
407 else if (config.chroma_subsampling_x == 0 &&
408 config.chroma_subsampling_y == 0) {
409 return heif_chroma_444;
410 }
411 else {
412 return heif_chroma_undefined;
413 }
414 }
415
416
417 assert(false);
418 return heif_chroma_undefined;
419 }
420
421
get_luma_bits_per_pixel_from_configuration(heif_item_id imageID) const422 int HeifFile::get_luma_bits_per_pixel_from_configuration(heif_item_id imageID) const
423 {
424 std::string image_type = get_item_type(imageID);
425
426 // HEVC
427
428 if (image_type == "hvc1") {
429 auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("hvcC"));
430 std::shared_ptr<Box_hvcC> hvcC_box = std::dynamic_pointer_cast<Box_hvcC>(box);
431 if (hvcC_box) {
432 return hvcC_box->get_configuration().bit_depth_luma;
433 }
434 }
435
436
437 // AV1
438
439 if (image_type == "av01") {
440 auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("av1C"));
441 std::shared_ptr<Box_av1C> av1C_box = std::dynamic_pointer_cast<Box_av1C>(box);
442 if (av1C_box) {
443 Box_av1C::configuration config = av1C_box->get_configuration();
444 if (!config.high_bitdepth) {
445 return 8;
446 }
447 else if (config.twelve_bit) {
448 return 12;
449 }
450 else {
451 return 10;
452 }
453 }
454 }
455
456 return -1;
457 }
458
459
get_chroma_bits_per_pixel_from_configuration(heif_item_id imageID) const460 int HeifFile::get_chroma_bits_per_pixel_from_configuration(heif_item_id imageID) const
461 {
462 std::string image_type = get_item_type(imageID);
463
464 // HEVC
465
466 if (image_type == "hvc1") {
467 auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("hvcC"));
468 std::shared_ptr<Box_hvcC> hvcC_box = std::dynamic_pointer_cast<Box_hvcC>(box);
469 if (hvcC_box) {
470 return hvcC_box->get_configuration().bit_depth_chroma;
471 }
472 }
473
474 // AV1
475
476 if (image_type == "av01") {
477 auto box = m_ipco_box->get_property_for_item_ID(imageID, m_ipma_box, fourcc("av1C"));
478 std::shared_ptr<Box_av1C> av1C_box = std::dynamic_pointer_cast<Box_av1C>(box);
479 if (av1C_box) {
480 Box_av1C::configuration config = av1C_box->get_configuration();
481 if (!config.high_bitdepth) {
482 return 8;
483 }
484 else if (config.twelve_bit) {
485 return 12;
486 }
487 else {
488 return 10;
489 }
490 }
491 }
492
493 return -1;
494 }
495
496
get_compressed_image_data(heif_item_id ID,std::vector<uint8_t> * data) const497 Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector<uint8_t>* data) const
498 {
499 #if ENABLE_PARALLEL_TILE_DECODING
500 std::lock_guard<std::mutex> guard(m_read_mutex);
501 #endif
502
503 if (!image_exists(ID)) {
504 return Error(heif_error_Usage_error,
505 heif_suberror_Nonexisting_item_referenced);
506 }
507
508 auto infe_box = get_infe(ID);
509 if (!infe_box) {
510 return Error(heif_error_Usage_error,
511 heif_suberror_Nonexisting_item_referenced);
512 }
513
514
515 std::string item_type = infe_box->get_item_type();
516 std::string content_type = infe_box->get_content_type();
517
518 // --- get coded image data pointers
519
520 auto items = m_iloc_box->get_items();
521 const Box_iloc::Item* item = nullptr;
522 for (const auto& i : items) {
523 if (i.item_ID == ID) {
524 item = &i;
525 break;
526 }
527 }
528 if (!item) {
529 std::stringstream sstr;
530 sstr << "Item with ID " << ID << " has no compressed data";
531
532 return Error(heif_error_Invalid_input,
533 heif_suberror_No_item_data,
534 sstr.str());
535 }
536
537 Error error = Error(heif_error_Unsupported_feature,
538 heif_suberror_Unsupported_codec);
539 if (item_type == "hvc1") {
540 // --- --- --- HEVC
541
542 // --- get properties for this image
543
544 std::vector<Box_ipco::Property> properties;
545 Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties);
546 if (err) {
547 return err;
548 }
549
550 // --- get codec configuration
551
552 std::shared_ptr<Box_hvcC> hvcC_box;
553 for (auto& prop : properties) {
554 if (prop.property->get_short_type() == fourcc("hvcC")) {
555 hvcC_box = std::dynamic_pointer_cast<Box_hvcC>(prop.property);
556 if (hvcC_box) {
557 break;
558 }
559 }
560 }
561
562 if (!hvcC_box) {
563 // Should always have an hvcC box, because we are checking this in
564 // heif_context::interpret_heif_file()
565 assert(false);
566 return Error(heif_error_Invalid_input,
567 heif_suberror_No_hvcC_box);
568 }
569 else if (!hvcC_box->get_headers(data)) {
570 return Error(heif_error_Invalid_input,
571 heif_suberror_No_item_data);
572 }
573
574 error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data);
575 }
576 else if (item_type == "av01") {
577 // --- --- --- AV1
578
579 // --- get properties for this image
580
581 std::vector<Box_ipco::Property> properties;
582 Error err = m_ipco_box->get_properties_for_item_ID(ID, m_ipma_box, properties);
583 if (err) {
584 return err;
585 }
586
587 // --- get codec configuration
588
589 std::shared_ptr<Box_av1C> av1C_box;
590 for (auto& prop : properties) {
591 if (prop.property->get_short_type() == fourcc("av1C")) {
592 av1C_box = std::dynamic_pointer_cast<Box_av1C>(prop.property);
593 if (av1C_box) {
594 break;
595 }
596 }
597 }
598
599 if (!av1C_box) {
600 // Should always have an hvcC box, because we are checking this in
601 // heif_context::interpret_heif_file()
602 return Error(heif_error_Invalid_input,
603 heif_suberror_No_av1C_box);
604 }
605 else if (!av1C_box->get_headers(data)) {
606 return Error(heif_error_Invalid_input,
607 heif_suberror_No_item_data);
608 }
609
610 error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data);
611 }
612 else if (true || // fallback case for all kinds of generic metadata (e.g. 'iptc')
613 item_type == "grid" ||
614 item_type == "iovl" ||
615 item_type == "Exif" ||
616 (item_type == "mime" && content_type == "application/rdf+xml")) {
617 error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data);
618 }
619
620 if (error != Error::Ok) {
621 return error;
622 }
623
624 return Error::Ok;
625 }
626
627
get_unused_item_id() const628 heif_item_id HeifFile::get_unused_item_id() const
629 {
630 for (heif_item_id id = 1;;
631 id++) {
632
633 bool id_exists = false;
634
635 for (const auto& infe : m_infe_boxes) {
636 if (infe.second->get_item_ID() == id) {
637 id_exists = true;
638 break;
639 }
640 }
641
642 if (!id_exists) {
643 return id;
644 }
645 }
646
647 assert(false); // should never be reached
648 return 0;
649 }
650
651
add_new_image(const char * item_type)652 heif_item_id HeifFile::add_new_image(const char* item_type)
653 {
654 auto box = add_new_infe_box(item_type);
655 return box->get_item_ID();
656 }
657
658
add_new_infe_box(const char * item_type)659 std::shared_ptr<Box_infe> HeifFile::add_new_infe_box(const char* item_type)
660 {
661 heif_item_id id = get_unused_item_id();
662
663 auto infe = std::make_shared<Box_infe>();
664 infe->set_item_ID(id);
665 infe->set_hidden_item(false);
666 infe->set_item_type(item_type);
667
668 m_infe_boxes[id] = infe;
669 m_iinf_box->append_child_box(infe);
670
671 return infe;
672 }
673
674
add_ispe_property(heif_item_id id,uint32_t width,uint32_t height)675 void HeifFile::add_ispe_property(heif_item_id id, uint32_t width, uint32_t height)
676 {
677 auto ispe = std::make_shared<Box_ispe>();
678 ispe->set_size(width, height);
679
680 int index = m_ipco_box->append_child_box(ispe);
681
682 m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{false, uint16_t(index + 1)});
683 }
684
add_clap_property(heif_item_id id,uint32_t clap_width,uint32_t clap_height,uint32_t image_width,uint32_t image_height)685 void HeifFile::add_clap_property(heif_item_id id, uint32_t clap_width, uint32_t clap_height,
686 uint32_t image_width, uint32_t image_height)
687 {
688 auto clap = std::make_shared<Box_clap>();
689 clap->set(clap_width, clap_height, image_width, image_height);
690
691 int index = m_ipco_box->append_child_box(clap);
692
693 m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
694 }
695
696
add_pixi_property(heif_item_id id,uint8_t c1,uint8_t c2,uint8_t c3)697 void HeifFile::add_pixi_property(heif_item_id id, uint8_t c1, uint8_t c2, uint8_t c3)
698 {
699 auto pixi = std::make_shared<Box_pixi>();
700 pixi->add_channel_bits(c1);
701 if (c2 || c3) {
702 pixi->add_channel_bits(c2);
703 pixi->add_channel_bits(c3);
704 }
705
706 int index = m_ipco_box->append_child_box(pixi);
707
708 m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
709 }
710
711
add_hvcC_property(heif_item_id id)712 void HeifFile::add_hvcC_property(heif_item_id id)
713 {
714 auto hvcC = std::make_shared<Box_hvcC>();
715 int index = m_ipco_box->append_child_box(hvcC);
716
717 m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
718 }
719
720
append_hvcC_nal_data(heif_item_id id,const std::vector<uint8_t> & nal_data)721 Error HeifFile::append_hvcC_nal_data(heif_item_id id, const std::vector<uint8_t>& nal_data)
722 {
723 auto hvcC = std::dynamic_pointer_cast<Box_hvcC>(m_ipco_box->get_property_for_item_ID(id,
724 m_ipma_box,
725 fourcc("hvcC")));
726
727 if (hvcC) {
728 hvcC->append_nal_data(nal_data);
729 return Error::Ok;
730 }
731 else {
732 // Should always have an hvcC box, because we are checking this in
733 // heif_context::interpret_heif_file()
734 assert(false);
735 return Error(heif_error_Usage_error,
736 heif_suberror_No_hvcC_box);
737 }
738 }
739
740
set_hvcC_configuration(heif_item_id id,const Box_hvcC::configuration & config)741 Error HeifFile::set_hvcC_configuration(heif_item_id id, const Box_hvcC::configuration& config)
742 {
743 auto hvcC = std::dynamic_pointer_cast<Box_hvcC>(m_ipco_box->get_property_for_item_ID(id,
744 m_ipma_box,
745 fourcc("hvcC")));
746
747 if (hvcC) {
748 hvcC->set_configuration(config);
749 return Error::Ok;
750 }
751 else {
752 return Error(heif_error_Usage_error,
753 heif_suberror_No_hvcC_box);
754 }
755 }
756
757
append_hvcC_nal_data(heif_item_id id,const uint8_t * data,size_t size)758 Error HeifFile::append_hvcC_nal_data(heif_item_id id, const uint8_t* data, size_t size)
759 {
760 std::vector<Box_ipco::Property> properties;
761
762 auto hvcC = std::dynamic_pointer_cast<Box_hvcC>(m_ipco_box->get_property_for_item_ID(id,
763 m_ipma_box,
764 fourcc("hvcC")));
765
766 if (hvcC) {
767 hvcC->append_nal_data(data, size);
768 return Error::Ok;
769 }
770 else {
771 return Error(heif_error_Usage_error,
772 heif_suberror_No_hvcC_box);
773 }
774 }
775
776
add_av1C_property(heif_item_id id)777 void HeifFile::add_av1C_property(heif_item_id id)
778 {
779 auto av1C = std::make_shared<Box_av1C>();
780 int index = m_ipco_box->append_child_box(av1C);
781
782 m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
783 }
784
785
set_av1C_configuration(heif_item_id id,const Box_av1C::configuration & config)786 Error HeifFile::set_av1C_configuration(heif_item_id id, const Box_av1C::configuration& config)
787 {
788 auto av1C = std::dynamic_pointer_cast<Box_av1C>(m_ipco_box->get_property_for_item_ID(id,
789 m_ipma_box,
790 fourcc("av1C")));
791
792 if (av1C) {
793 av1C->set_configuration(config);
794 return Error::Ok;
795 }
796 else {
797 return Error(heif_error_Usage_error,
798 heif_suberror_No_av1C_box);
799 }
800 }
801
802
append_iloc_data(heif_item_id id,const std::vector<uint8_t> & nal_packets,uint8_t construction_method)803 void HeifFile::append_iloc_data(heif_item_id id, const std::vector<uint8_t>& nal_packets, uint8_t construction_method)
804 {
805 m_iloc_box->append_data(id, nal_packets, construction_method);
806 }
807
808
append_iloc_data_with_4byte_size(heif_item_id id,const uint8_t * data,size_t size)809 void HeifFile::append_iloc_data_with_4byte_size(heif_item_id id, const uint8_t* data, size_t size)
810 {
811 std::vector<uint8_t> nal;
812 nal.resize(size + 4);
813
814 nal[0] = (uint8_t) ((size >> 24) & 0xFF);
815 nal[1] = (uint8_t) ((size >> 16) & 0xFF);
816 nal[2] = (uint8_t) ((size >> 8) & 0xFF);
817 nal[3] = (uint8_t) ((size >> 0) & 0xFF);
818
819 memcpy(nal.data() + 4, data, size);
820
821 append_iloc_data(id, nal);
822 }
823
set_primary_item_id(heif_item_id id)824 void HeifFile::set_primary_item_id(heif_item_id id)
825 {
826 m_pitm_box->set_item_ID(id);
827 }
828
add_iref_reference(uint32_t type,heif_item_id from,std::vector<heif_item_id> to)829 void HeifFile::add_iref_reference(uint32_t type, heif_item_id from,
830 std::vector<heif_item_id> to)
831 {
832 if (!m_iref_box) {
833 m_iref_box = std::make_shared<Box_iref>();
834 m_meta_box->append_child_box(m_iref_box);
835 }
836
837 m_iref_box->add_reference(type, from, to);
838 }
839
set_auxC_property(heif_item_id id,std::string type)840 void HeifFile::set_auxC_property(heif_item_id id, std::string type)
841 {
842 auto auxC = std::make_shared<Box_auxC>();
843 auxC->set_aux_type(type);
844
845 int index = m_ipco_box->append_child_box(auxC);
846
847 m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
848 }
849
set_color_profile(heif_item_id id,const std::shared_ptr<const color_profile> profile)850 void HeifFile::set_color_profile(heif_item_id id, const std::shared_ptr<const color_profile> profile)
851 {
852 auto colr = std::make_shared<Box_colr>();
853 colr->set_color_profile(profile);
854
855 int index = m_ipco_box->append_child_box(colr);
856 m_ipma_box->add_property_for_item_ID(id, Box_ipma::PropertyAssociation{true, uint16_t(index + 1)});
857 }
858
859
860 // TODO: the hdlr box is probably not the right place for this. Into which box should we write comments?
set_hdlr_library_info(std::string encoder_plugin_version)861 void HeifFile::set_hdlr_library_info(std::string encoder_plugin_version)
862 {
863 std::stringstream sstr;
864 sstr << "libheif (" << LIBHEIF_VERSION << ") / " << encoder_plugin_version;
865 m_hdlr_box->set_name(sstr.str());
866 }
867
868
869 #ifdef _MSC_VER
convert_utf8_path_to_utf16(std::string str)870 std::wstring HeifFile::convert_utf8_path_to_utf16(std::string str)
871 {
872 std::wstring ret;
873 int len = MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), NULL, 0);
874 if (len > 0)
875 {
876 ret.resize(len);
877 MultiByteToWideChar(CP_UTF8, 0, str.c_str(), str.length(), &ret[0], len);
878 }
879 return ret;
880 }
881 #endif
882