From b671d752d13c8273df21f8e6e18e7772420fef11 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Tue, 13 Feb 2024 12:27:42 +1100 Subject: [PATCH 1/6] iloc: fix error message --- libheif/box.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libheif/box.cc b/libheif/box.cc index 6d101e46ac..1375f2bb00 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -1275,9 +1275,9 @@ Error Box_iloc::read_data(const Item& item, } else { std::stringstream sstr; - sstr << "Item construction method " << item.construction_method << " not implemented"; + sstr << "Item construction method " << (int) item.construction_method << " not implemented"; return Error(heif_error_Unsupported_feature, - heif_suberror_No_idat_box, + heif_suberror_Unsupported_item_construction_method, sstr.str()); } } From 5cbc793994baf12e8c8070344bf9029df627e379 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Tue, 13 Feb 2024 19:47:18 +1100 Subject: [PATCH 2/6] wip: adding construction_method 2 support --- go/heif/heif.go | 2 + libheif/box.cc | 183 ++++++++++++++++++++++++++------------ libheif/box.h | 6 ++ libheif/error.cc | 2 + libheif/file.cc | 13 +-- libheif/file.h | 1 + libheif/heif.h | 2 + libheif/heif_emscripten.h | 1 + 8 files changed, 145 insertions(+), 65 deletions(-) diff --git a/go/heif/heif.go b/go/heif/heif.go index ad2fa5b641..303e5b2eeb 100644 --- a/go/heif/heif.go +++ b/go/heif/heif.go @@ -216,6 +216,8 @@ const ( SuberrorNoIrefBox = C.heif_suberror_No_iref_box + SuberrorNoDinfBox = C.heif_suberror_No_dinf_box + SuberrorNoPictHandler = C.heif_suberror_No_pict_handler // An item property referenced in the 'ipma' box is not existing in the 'ipco' container. diff --git a/libheif/box.cc b/libheif/box.cc index 1375f2bb00..892d8e1b48 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -35,6 +35,7 @@ #include #include #include +#include #include #include @@ -1188,76 +1189,81 @@ std::string Box_iloc::dump(Indent& indent) const return sstr.str(); } +bool Box_iloc::read_extent(const Item& item, + const std::shared_ptr& istr, + const Box_iloc::Extent extent, + std::vector* dest) const { + // --- security check that we do not allocate too much memory + size_t old_size = dest->size(); + if (MAX_MEMORY_BLOCK_SIZE - old_size < extent.length) { + std::stringstream sstr; + sstr << "iloc box contained " << extent.length << " bytes, total memory size would be " + << (old_size + extent.length) << " bytes, exceeding the security limit of " + << MAX_MEMORY_BLOCK_SIZE << " bytes"; -Error Box_iloc::read_data(const Item& item, - const std::shared_ptr& istr, - const std::shared_ptr& idat, - std::vector* dest) const -{ - // TODO: this function should always append the data to the output vector as this is used when - // the image data is concatenated with data in a configuration box. However, it seems that - // this function clears the array in some cases. This should be corrected. - - for (const auto& extent : item.extents) { - if (item.construction_method == 0) { - - // --- security check that we do not allocate too much memory - - size_t old_size = dest->size(); - if (MAX_MEMORY_BLOCK_SIZE - old_size < extent.length) { - std::stringstream sstr; - sstr << "iloc box contained " << extent.length << " bytes, total memory size would be " - << (old_size + extent.length) << " bytes, exceeding the security limit of " - << MAX_MEMORY_BLOCK_SIZE << " bytes"; - - return Error(heif_error_Memory_allocation_error, - heif_suberror_Security_limit_exceeded, - sstr.str()); - } + return Error(heif_error_Memory_allocation_error, + heif_suberror_Security_limit_exceeded, + sstr.str()); + } + // --- make sure that all data is available + if (extent.offset > MAX_FILE_POS || + item.base_offset > MAX_FILE_POS || + extent.length > MAX_FILE_POS) { + return Error(heif_error_Invalid_input, + heif_suberror_Security_limit_exceeded, + "iloc data pointers out of allowed range"); + } - // --- make sure that all data is available + StreamReader::grow_status status = istr->wait_for_file_size(extent.offset + item.base_offset + extent.length); + if (status == StreamReader::size_beyond_eof) { + // Out-of-bounds + // TODO: I think we should not clear this. Maybe we want to try reading again later and + // hence should not lose the data already read. + dest->clear(); - if (extent.offset > MAX_FILE_POS || - item.base_offset > MAX_FILE_POS || - extent.length > MAX_FILE_POS) { - return Error(heif_error_Invalid_input, - heif_suberror_Security_limit_exceeded, - "iloc data pointers out of allowed range"); - } + std::stringstream sstr; + sstr << "Extent in iloc box references data outside of file bounds " + << "(points to file position " << extent.offset + item.base_offset << ")\n"; - StreamReader::grow_status status = istr->wait_for_file_size(extent.offset + item.base_offset + extent.length); - if (status == StreamReader::size_beyond_eof) { - // Out-of-bounds - // TODO: I think we should not clear this. Maybe we want to try reading again later and - // hence should not lose the data already read. - dest->clear(); + return Error(heif_error_Invalid_input, + heif_suberror_End_of_data, + sstr.str()); + } + else if (status == StreamReader::timeout) { + // TODO: maybe we should introduce some 'Recoverable error' instead of 'Invalid input' + return Error(heif_error_Invalid_input, + heif_suberror_End_of_data); + } - std::stringstream sstr; - sstr << "Extent in iloc box references data outside of file bounds " - << "(points to file position " << extent.offset + item.base_offset << ")\n"; + // --- move file pointer to start of data - return Error(heif_error_Invalid_input, - heif_suberror_End_of_data, - sstr.str()); - } - else if (status == StreamReader::timeout) { - // TODO: maybe we should introduce some 'Recoverable error' instead of 'Invalid input' - return Error(heif_error_Invalid_input, - heif_suberror_End_of_data); - } + bool success = istr->seek(extent.offset + item.base_offset); + assert(success); + (void) success; - // --- move file pointer to start of data - bool success = istr->seek(extent.offset + item.base_offset); - assert(success); - (void) success; + // --- read data + dest->resize(static_cast(old_size + extent.length)); + success = istr->read((char*) dest->data() + old_size, static_cast(extent.length)); + assert(success); + return success; +} - // --- read data +Error Box_iloc::read_data(const Item& item, + const std::shared_ptr& istr, + const std::shared_ptr& idat, + const std::shared_ptr& dinf, + std::vector* dest) const +{ + // TODO: this function should always append the data to the output vector as this is used when + // the image data is concatenated with data in a configuration box. However, it seems that + // this function clears the array in some cases. This should be corrected. - dest->resize(static_cast(old_size + extent.length)); - success = istr->read((char*) dest->data() + old_size, static_cast(extent.length)); + for (const auto& extent : item.extents) { + if (item.construction_method == 0) { + bool success = read_extent(item, istr, extent, dest); assert(success); (void) success; } @@ -1265,7 +1271,7 @@ Error Box_iloc::read_data(const Item& item, if (!idat) { return Error(heif_error_Invalid_input, heif_suberror_No_idat_box, - "idat box referenced in iref box is not present in file"); + "idat box referenced in iloc box is not present in file"); } idat->read_data(istr, @@ -1273,6 +1279,65 @@ Error Box_iloc::read_data(const Item& item, extent.length, *dest); } + else if (item.construction_method == 2) { + if (item.data_reference_index == 0) { + bool success = read_extent(item, istr, extent, dest); + assert(success); + (void) success; + } else { + if (!dinf) { + return Error(heif_error_Invalid_input, + heif_suberror_No_dinf_box, + "dinf box referenced in iloc box is not present in file"); + + } + if (dinf->get_child_boxes(fourcc_to_uint32("dref")).size() != 1) { + return Error(heif_error_Invalid_input, + heif_suberror_No_dinf_box, + "dinf box is incomplete - missing dref child box"); + } + std::shared_ptr dref = std::dynamic_pointer_cast(dinf->get_child_boxes(fourcc_to_uint32("dref"))[0]); + if (dref->get_all_child_boxes().size() < item.data_reference_index) { + std::stringstream sstr; + sstr << "Item construction method requires data references that are not present"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_item_construction_method, + sstr.str()); + } + std::shared_ptr dataentry = dref->get_all_child_boxes()[(item.data_reference_index - 1)]; + if (dataentry->get_short_type() == fourcc_to_uint32("url ")) { + std::shared_ptr urlBox = std::dynamic_pointer_cast(dataentry); + if (urlBox->get_flags() == 0x000001) { + bool success = read_extent(item, istr, extent, dest); + assert(success); + (void) success; + } else { + std::string location = urlBox->get_location(); +#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER) + auto input_stream_istr = std::unique_ptr(new std::ifstream(convert_utf8_path_to_utf16(location).c_str(), std::ios_base::binary)); +#else + auto datafile_istr = std::unique_ptr(new std::ifstream(location.c_str(), std::ios_base::binary)); +#endif + if (!datafile_istr->good()) { + std::stringstream sstr; + sstr << "Error opening file: " << location << ", " << strerror(errno) << " (" << errno << ")\n"; + return Error(heif_error_Input_does_not_exist, heif_suberror_Unspecified, sstr.str()); + } + + auto datafileReader = std::make_shared(std::move(datafile_istr)); + bool success = read_extent(item, datafileReader, extent, dest); + assert(success); + (void) success; + } + } else { + std::stringstream sstr; + sstr << "Item construction method 2 with data reference type " << dataentry->get_type_string() << "is not implemented"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_item_construction_method, + sstr.str()); + } + } + } else { std::stringstream sstr; sstr << "Item construction method " << (int) item.construction_method << " not implemented"; diff --git a/libheif/box.h b/libheif/box.h index 0c0b148d12..7b41345d89 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -391,6 +391,7 @@ class Box_iloc : public FullBox Error read_data(const Item& item, const std::shared_ptr& istr, const std::shared_ptr&, + const std::shared_ptr&, std::vector* dest) const; void set_min_version(uint8_t min_version) { m_user_defined_min_version = min_version; } @@ -430,6 +431,10 @@ class Box_iloc : public FullBox uint8_t m_index_size = 0; void patch_iloc_header(StreamWriter& writer) const; + bool read_extent(const Item& item, + const std::shared_ptr& istr, + const Box_iloc::Extent extent, + std::vector* dest) const; int m_idat_offset = 0; // only for writing: offset of next data array }; @@ -857,6 +862,7 @@ class Box_url : public FullBox { public: std::string dump(Indent&) const override; + std::string get_location() const { return m_location; }; protected: Error parse(BitstreamRange& range) override; diff --git a/libheif/error.cc b/libheif/error.cc index db8d6eaaa0..f0b7e49c0f 100644 --- a/libheif/error.cc +++ b/libheif/error.cc @@ -120,6 +120,8 @@ const char* Error::get_error_string(heif_suberror_code err) return "No 'iref' box"; case heif_suberror_No_infe_box: return "No 'infe' box"; + case heif_suberror_No_dinf_box: + return "No 'dinf' box"; case heif_suberror_No_pict_handler: return "Not a 'pict' handler"; case heif_suberror_Ipma_box_references_nonexisting_property: diff --git a/libheif/file.cc b/libheif/file.cc index 4f2a509842..65e5ca8040 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -377,6 +377,7 @@ Error HeifFile::parse_heif_file(BitstreamRange& range) heif_suberror_No_iinf_box); } + m_dinf_box = std::dynamic_pointer_cast(m_meta_box->get_child_box(fourcc("dinf"))); // --- build list of images @@ -775,7 +776,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* heif_suberror_No_item_data); } - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); } else if (item_type == "av01") { // --- --- --- AV1 @@ -811,7 +812,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* heif_suberror_No_item_data); } - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); } else if (item_type == "jpeg" || (item_type == "mime" && get_content_type(ID) == "image/jpeg")) { @@ -837,7 +838,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* } } - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); } else if (item_type == "j2k1") { std::vector> properties; @@ -871,7 +872,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* // heif_suberror_No_item_data); // } - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); } else if (true || // fallback case for all kinds of generic metadata (e.g. 'iptc') item_type == "grid" || @@ -886,7 +887,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* #if WITH_DEFLATE_HEADER_COMPRESSION read_uncompressed = false; std::vector compressed_data; - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, &compressed_data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, &compressed_data); *data = inflate(compressed_data); #else return Error(heif_error_Unsupported_feature, @@ -897,7 +898,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* } if (read_uncompressed) { - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); } } diff --git a/libheif/file.h b/libheif/file.h index 881aabccc6..2ea8a22f27 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -222,6 +222,7 @@ class HeifFile std::shared_ptr m_iinf_box; std::shared_ptr m_iprp_box; + std::shared_ptr m_dinf_box; std::map > m_infe_boxes; diff --git a/libheif/heif.h b/libheif/heif.h index 1c3eb7089d..da968ff0ab 100644 --- a/libheif/heif.h +++ b/libheif/heif.h @@ -232,6 +232,8 @@ enum heif_suberror_code // Invalid JPEG 2000 codestream - usually a missing marker heif_suberror_Invalid_J2K_codestream = 140, + heif_suberror_No_dinf_box = 141, + // --- Memory_allocation_error --- diff --git a/libheif/heif_emscripten.h b/libheif/heif_emscripten.h index fe79be19f6..27bcc81f43 100644 --- a/libheif/heif_emscripten.h +++ b/libheif/heif_emscripten.h @@ -321,6 +321,7 @@ EMSCRIPTEN_BINDINGS(libheif) { .value("heif_suberror_No_iinf_box", heif_suberror_No_iinf_box) .value("heif_suberror_No_iprp_box", heif_suberror_No_iprp_box) .value("heif_suberror_No_iref_box", heif_suberror_No_iref_box) + .value("heif_suberror_No_dinf_box", heif_suberror_No_dinf_box) .value("heif_suberror_No_pict_handler", heif_suberror_No_pict_handler) .value("heif_suberror_Ipma_box_references_nonexisting_property", heif_suberror_Ipma_box_references_nonexisting_property) .value("heif_suberror_No_properties_assigned_to_item", heif_suberror_No_properties_assigned_to_item) From 72c2bb28bb1bd3fa307d8cea7889325e34fdc10e Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Wed, 14 Feb 2024 20:58:09 +1100 Subject: [PATCH 3/6] wip: add support for relative path names This uses a C++-17 library feature for filesystem, which is pretty widely supported but still an additional requirement --- libheif/CMakeLists.txt | 2 ++ libheif/box.cc | 14 ++++++++------ libheif/box.h | 2 ++ libheif/file.cc | 18 ++++++++++++------ libheif/file.h | 2 ++ tests/CMakeLists.txt | 1 + 6 files changed, 27 insertions(+), 12 deletions(-) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 818a37b5ff..3913797862 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -116,6 +116,8 @@ target_compile_definitions(heif LIBHEIF_EXPORTS HAVE_VISIBILITY) +target_compile_features(heif PRIVATE cxx_std_17) + if (PLUGIN_LOADING_SUPPORTED_AND_ENABLED) target_compile_definitions(heif PRIVATE ENABLE_PLUGIN_LOADING=1) target_link_libraries(heif PRIVATE ${CMAKE_DL_LIBS}) diff --git a/libheif/box.cc b/libheif/box.cc index 892d8e1b48..7754c90298 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -1255,6 +1255,7 @@ Error Box_iloc::read_data(const Item& item, const std::shared_ptr& istr, const std::shared_ptr& idat, const std::shared_ptr& dinf, + const std::filesystem::path base_path, std::vector* dest) const { // TODO: this function should always append the data to the output vector as this is used when @@ -1313,11 +1314,12 @@ Error Box_iloc::read_data(const Item& item, (void) success; } else { std::string location = urlBox->get_location(); -#if defined(__MINGW32__) || defined(__MINGW64__) || defined(_MSC_VER) - auto input_stream_istr = std::unique_ptr(new std::ifstream(convert_utf8_path_to_utf16(location).c_str(), std::ios_base::binary)); -#else - auto datafile_istr = std::unique_ptr(new std::ifstream(location.c_str(), std::ios_base::binary)); -#endif + // TODO: handle case where its really a URL + std::filesystem::path locationPath(location); + if (locationPath.is_relative()) { + locationPath = base_path / locationPath; + } + auto datafile_istr = std::unique_ptr(new std::ifstream(locationPath, std::ios_base::binary)); if (!datafile_istr->good()) { std::stringstream sstr; sstr << "Error opening file: " << location << ", " << strerror(errno) << " (" << errno << ")\n"; @@ -1331,7 +1333,7 @@ Error Box_iloc::read_data(const Item& item, } } else { std::stringstream sstr; - sstr << "Item construction method 2 with data reference type " << dataentry->get_type_string() << "is not implemented"; + sstr << "Item construction method 2 with data reference type " << dataentry->get_type_string() << " is not implemented"; return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_item_construction_method, sstr.str()); diff --git a/libheif/box.h b/libheif/box.h index 7b41345d89..75e00556b6 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -34,6 +34,7 @@ #include #include #include +#include #include "error.h" #include "heif.h" @@ -392,6 +393,7 @@ class Box_iloc : public FullBox const std::shared_ptr& istr, const std::shared_ptr&, const std::shared_ptr&, + const std::filesystem::path base_path, std::vector* dest) const; void set_min_version(uint8_t min_version) { m_user_defined_min_version = min_version; } diff --git a/libheif/file.cc b/libheif/file.cc index 65e5ca8040..929ec4df74 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -27,6 +27,7 @@ #include "libheif/vvc.h" #include +#include #include #include #include @@ -83,7 +84,12 @@ Error HeifFile::read_from_file(const char* input_filename) sstr << "Error opening file: " << strerror(errno) << " (" << errno << ")\n"; return Error(heif_error_Input_does_not_exist, heif_suberror_Unspecified, sstr.str()); } - + std::filesystem::path input_path(input_filename); + if (input_path.has_parent_path()) { + m_base_path = input_path.parent_path(); + } else { + m_base_path.clear(); + } auto input_stream = std::make_shared(std::move(input_stream_istr)); return read(input_stream); } @@ -776,7 +782,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* heif_suberror_No_item_data); } - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, m_base_path, data); } else if (item_type == "av01") { // --- --- --- AV1 @@ -812,7 +818,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* heif_suberror_No_item_data); } - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, m_base_path, data); } else if (item_type == "jpeg" || (item_type == "mime" && get_content_type(ID) == "image/jpeg")) { @@ -838,7 +844,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* } } - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, m_base_path, data); } else if (item_type == "j2k1") { std::vector> properties; @@ -872,7 +878,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* // heif_suberror_No_item_data); // } - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, m_base_path, data); } else if (true || // fallback case for all kinds of generic metadata (e.g. 'iptc') item_type == "grid" || @@ -898,7 +904,7 @@ Error HeifFile::get_compressed_image_data(heif_item_id ID, std::vector* } if (read_uncompressed) { - error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, data); + error = m_iloc_box->read_data(*item, m_input_stream, m_idat_box, m_dinf_box, m_base_path, data); } } diff --git a/libheif/file.h b/libheif/file.h index 2ea8a22f27..c59c4ed377 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -26,6 +26,7 @@ #include "hevc.h" #include "nclx.h" +#include #include #include #include @@ -206,6 +207,7 @@ class HeifFile #endif std::shared_ptr m_input_stream; + std::filesystem::path m_base_path; std::vector > m_top_level_boxes; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 6d619b7070..57fe4f9ee2 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,6 +9,7 @@ configure_file(test-config.cc.in ${CMAKE_BINARY_DIR}/generated/test-config.cc) macro(add_libheif_test TEST_FILE) set(TEST_NAME ${TEST_FILE}) add_executable(${TEST_NAME} main.cc catch.hpp ${CMAKE_BINARY_DIR}/generated/test-config.cc test_utils.cc ${TEST_FILE}.cc) + target_compile_features(${TEST_NAME} PRIVATE cxx_std_17) target_link_libraries(${TEST_NAME} PRIVATE heif) add_test(NAME ${TEST_NAME} COMMAND ./${TEST_NAME}) endmacro() From 28b065ea3205a17160f037bfe3f945e20a18edf5 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Thu, 15 Feb 2024 12:37:20 +1100 Subject: [PATCH 4/6] Update CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b56dabad04..9ff397b393 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,7 +41,7 @@ if(NOT MSVC) endif () endif() -set(CMAKE_CXX_STANDARD 11) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) From 3e1e04de878a9d1affb3d5896fb6800bb7495930 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Thu, 22 Feb 2024 18:23:55 +1100 Subject: [PATCH 5/6] wip on remote access - DO NOT MERGE --- CMakeLists.txt | 9 +++++++++ libheif/CMakeLists.txt | 2 -- libheif/box.cc | 37 +++++++++++++++++++++++-------------- libheif/security_limits.h | 2 +- tests/CMakeLists.txt | 1 - 5 files changed, 33 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ff397b393..4f8a2f9083 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -197,6 +197,8 @@ endif() option(WITH_UNCOMPRESSED_CODEC " Support internal ISO/IEC 23001-17 uncompressed codec (experimental) " OFF) +# CURL +find_package(CURL) # --- show codec compilation summary @@ -301,6 +303,13 @@ else() message("libsharpyuv: disabled") endif () +# --- CURL download library -- +if(CURL_FOUND) + message("curl: found") +else() + message("curl: not found") +endif() + # --- Create libheif pkgconfig file set(prefix ${CMAKE_INSTALL_PREFIX}) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 3913797862..818a37b5ff 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -116,8 +116,6 @@ target_compile_definitions(heif LIBHEIF_EXPORTS HAVE_VISIBILITY) -target_compile_features(heif PRIVATE cxx_std_17) - if (PLUGIN_LOADING_SUPPORTED_AND_ENABLED) target_compile_definitions(heif PRIVATE ENABLE_PLUGIN_LOADING=1) target_link_libraries(heif PRIVATE ${CMAKE_DL_LIBS}) diff --git a/libheif/box.cc b/libheif/box.cc index 7754c90298..8f33707f43 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -1314,22 +1314,31 @@ Error Box_iloc::read_data(const Item& item, (void) success; } else { std::string location = urlBox->get_location(); - // TODO: handle case where its really a URL - std::filesystem::path locationPath(location); - if (locationPath.is_relative()) { - locationPath = base_path / locationPath; - } - auto datafile_istr = std::unique_ptr(new std::ifstream(locationPath, std::ios_base::binary)); - if (!datafile_istr->good()) { + if (location.rfind("https://", 0) == 0) { + // TODO std::stringstream sstr; - sstr << "Error opening file: " << location << ", " << strerror(errno) << " (" << errno << ")\n"; - return Error(heif_error_Input_does_not_exist, heif_suberror_Unspecified, sstr.str()); + sstr << "Item construction method 2 with https location of " << location << " is not implemented"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_item_construction_method, + sstr.str()); + } else { + // See if we can read as a local file + std::filesystem::path locationPath(location); + if (locationPath.is_relative()) { + locationPath = base_path / locationPath; + } + auto datafile_istr = std::unique_ptr(new std::ifstream(locationPath, std::ios_base::binary)); + if (!datafile_istr->good()) { + std::stringstream sstr; + sstr << "Error opening file: " << location << ", " << strerror(errno) << " (" << errno << ")\n"; + return Error(heif_error_Input_does_not_exist, heif_suberror_Unspecified, sstr.str()); + } + + auto datafileReader = std::make_shared(std::move(datafile_istr)); + bool success = read_extent(item, datafileReader, extent, dest); + assert(success); + (void) success; } - - auto datafileReader = std::make_shared(std::move(datafile_istr)); - bool success = read_extent(item, datafileReader, extent, dest); - assert(success); - (void) success; } } else { std::stringstream sstr; diff --git a/libheif/security_limits.h b/libheif/security_limits.h index f1c177c0a1..281b4e3cff 100644 --- a/libheif/security_limits.h +++ b/libheif/security_limits.h @@ -25,7 +25,7 @@ static const size_t MAX_CHILDREN_PER_BOX = 20000; static const int MAX_ILOC_ITEMS = 20000; -static const int MAX_ILOC_EXTENTS_PER_ITEM = 32; +static const int MAX_ILOC_EXTENTS_PER_ITEM = 1024; static const int MAX_MEMORY_BLOCK_SIZE = 512 * 1024 * 1024; // 512 MB static const int MAX_COLOR_PROFILE_SIZE = 100 * 1024 * 1024; // 100 MB diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 57fe4f9ee2..6d619b7070 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -9,7 +9,6 @@ configure_file(test-config.cc.in ${CMAKE_BINARY_DIR}/generated/test-config.cc) macro(add_libheif_test TEST_FILE) set(TEST_NAME ${TEST_FILE}) add_executable(${TEST_NAME} main.cc catch.hpp ${CMAKE_BINARY_DIR}/generated/test-config.cc test_utils.cc ${TEST_FILE}.cc) - target_compile_features(${TEST_NAME} PRIVATE cxx_std_17) target_link_libraries(${TEST_NAME} PRIVATE heif) add_test(NAME ${TEST_NAME} COMMAND ./${TEST_NAME}) endmacro() From 68c58da55b6660db36bac7c257063cb23a36f1be Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Mon, 26 Feb 2024 21:40:41 +1100 Subject: [PATCH 6/6] add remote fetching using libcurl --- libheif/CMakeLists.txt | 9 +++++++++ libheif/box.cc | 45 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 818a37b5ff..60011d1dfc 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -132,6 +132,15 @@ else () message("Not compiling 'libsharpyuv'") endif () +if (CURL_FOUND) + message("Compiling in 'libcurl'") + target_compile_definitions(heif PUBLIC HAVE_CURL=1) + target_include_directories(heif PRIVATE ${CURL_INCLUDE_DIRS}) + target_link_libraries(heif PRIVATE ${CURL_LIBRARIES}) +else () + message("Not compiling 'libcurl'") +endif () + if (WITH_DEFLATE_HEADER_COMPRESSION) find_package(ZLIB REQUIRED) target_link_libraries(heif PRIVATE ZLIB::ZLIB) diff --git a/libheif/box.cc b/libheif/box.cc index 8f33707f43..bc6d9d2643 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -43,6 +43,9 @@ #include "uncompressed_box.h" #endif +#if HAVE_CURL +#include +#endif Fraction::Fraction(int32_t num, int32_t den) { @@ -1251,6 +1254,15 @@ bool Box_iloc::read_extent(const Item& item, return success; } +static size_t memoryHandler(void *contents, size_t size, size_t nmemb, void *userp) +{ + std::vector *fileData = (std::vector*) userp; + size_t numBytes = size * nmemb; + uint8_t* data = (uint8_t*) contents; + fileData->insert(fileData->end(), data, data + numBytes); + return numBytes; +} + Error Box_iloc::read_data(const Item& item, const std::shared_ptr& istr, const std::shared_ptr& idat, @@ -1315,12 +1327,35 @@ Error Box_iloc::read_data(const Item& item, } else { std::string location = urlBox->get_location(); if (location.rfind("https://", 0) == 0) { - // TODO +#if HAVE_CURL + CURL *curl_handle; + CURLcode result; + curl_global_init(CURL_GLOBAL_ALL); + curl_handle = curl_easy_init(); + std::vector fileData; + curl_easy_setopt(curl_handle, CURLOPT_URL, location.c_str()); + curl_easy_setopt(curl_handle, CURLOPT_USERAGENT, "libheif/2.18.0"); + curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, memoryHandler); + curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, &fileData); + curl_easy_setopt(curl_handle, CURLOPT_BUFFERSIZE, 1024*1024); + result = curl_easy_perform(curl_handle); + curl_easy_cleanup(curl_handle); + if (result != CURLE_OK) { + std::stringstream sstr; + sstr << "Item construction method 2 with https location of " << location << " failed"; + return Error(heif_error_Unsupported_feature, + heif_suberror_Unsupported_item_construction_method, + sstr.str()); + } + auto memoryReader = std::make_shared(fileData.data(), fileData.size(), false); + bool success = read_extent(item, memoryReader, extent, dest); + assert(success); + (void) success; +#else std::stringstream sstr; - sstr << "Item construction method 2 with https location of " << location << " is not implemented"; - return Error(heif_error_Unsupported_feature, - heif_suberror_Unsupported_item_construction_method, - sstr.str()); + sstr << "Item construction method 2 with https location of " << location << " is not supported without libcurl"; + return Error(heif_error_Unsupported_feature, heif_suberror_Unsupported_item_construction_method, sstr.str()); +#endif } else { // See if we can read as a local file std::filesystem::path locationPath(location);