Commit 248c1de0 for libheif
commit 248c1de03b3264c24e6c515b8c86c2161744ae65
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Fri Feb 27 16:35:21 2026 +0100
allow adding MIME item to sequence only file
diff --git a/examples/heif_enc.cc b/examples/heif_enc.cc
index 2642bf61..8aa58ce3 100644
--- a/examples/heif_enc.cc
+++ b/examples/heif_enc.cc
@@ -1283,6 +1283,7 @@ public:
int do_encode_images(heif_context*, heif_encoder*, heif_encoding_options* options, const std::vector<std::string>& args);
int do_encode_sequence(heif_context*, heif_encoder*, heif_encoding_options* options, std::vector<std::string> args);
+int add_mime_item(heif_context* context);
int main(int argc, char** argv)
@@ -1672,10 +1673,6 @@ int main(int argc, char** argv)
return 5;
}
- if (encode_sequence && !option_mime_item_file.empty()) {
- std::cerr << "MIME item cannot be added to sequence-only files.\n";
- return 5;
- }
if (!option_sai_data_file.empty() && !encode_sequence) {
std::cerr << "Image SAI data can only be used with sequences.\n";
@@ -1889,6 +1886,15 @@ int main(int argc, char** argv)
return ret;
}
+ // --- add extra MIME item with user data
+
+ ret = add_mime_item(context.get());
+ if (ret != 0) {
+ heif_encoding_options_free(options);
+ heif_encoder_release(encoder);
+ return ret;
+ }
+
// --- write HEIF file
@@ -1909,6 +1915,48 @@ int main(int argc, char** argv)
}
+int add_mime_item(heif_context* context)
+{
+ if (!option_mime_item_file.empty() || !option_mime_item_type.empty()) {
+ if (option_mime_item_file.empty() || option_mime_item_type.empty()) {
+ std::cerr << "Options --add-mime-item and --mime-item-file have to be used together\n";
+ return 5;
+ }
+
+ std::ifstream istr(option_mime_item_file.c_str(), std::ios::binary | std::ios::ate);
+ if (!istr) {
+ std::cerr << "Failed to open file for MIME item: '" << option_mime_item_file << "'\n";
+ return 5;
+ }
+
+ // Get size by seeking to the end (thanks to ios::ate)
+ std::streamsize size = istr.tellg();
+ if (size < 0) {
+ std::cerr << "Querying size of file '" << option_mime_item_file << "' failed.\n";
+ return 5;
+ }
+
+ std::vector<uint8_t> buffer(size);
+
+ // Seek back to beginning and read
+ istr.seekg(0, std::ios::beg);
+ istr.read(reinterpret_cast<char*>(buffer.data()), size);
+
+ heif_item_id itemId;
+ heif_context_add_mime_item(context, option_mime_item_type.c_str(),
+ metadata_compression_method,
+ buffer.data(), (int)buffer.size(),
+ &itemId);
+
+ if (!option_mime_item_name.empty()) {
+ heif_item_set_item_name(context, itemId, option_mime_item_name.c_str());
+ }
+ }
+
+ return 0;
+}
+
+
int do_encode_images(heif_context* context, heif_encoder* encoder, heif_encoding_options* options, const std::vector<std::string>& args)
{
std::shared_ptr<heif_image> primary_image;
@@ -2221,44 +2269,6 @@ int do_encode_images(heif_context* context, heif_encoder* encoder, heif_encoding
}
#endif
- // --- add extra MIME item with user data
-
- if (!option_mime_item_file.empty() || !option_mime_item_type.empty()) {
- if (option_mime_item_file.empty() || option_mime_item_type.empty()) {
- std::cerr << "Options --add-mime-item and --mime-item-file have to be used together\n";
- return 5;
- }
-
- std::ifstream istr(option_mime_item_file.c_str(), std::ios::binary | std::ios::ate);
- if (!istr) {
- std::cerr << "Failed to open file for MIME item: '" << option_mime_item_file << "'\n";
- return 5;
- }
-
- // Get size by seeking to the end (thanks to ios::ate)
- std::streamsize size = istr.tellg();
- if (size < 0) {
- std::cerr << "Querying size of file '" << option_mime_item_file << "' failed.\n";
- return 5;
- }
-
- std::vector<uint8_t> buffer(size);
-
- // Seek back to beginning and read
- istr.seekg(0, std::ios::beg);
- istr.read(reinterpret_cast<char*>(buffer.data()), size);
-
- heif_item_id itemId;
- heif_context_add_mime_item(context, option_mime_item_type.c_str(),
- metadata_compression_method,
- buffer.data(), (int)buffer.size(),
- &itemId);
-
- if (!option_mime_item_name.empty()) {
- heif_item_set_item_name(context, itemId, option_mime_item_name.c_str());
- }
- }
-
if (run_benchmark) {
double psnr = compute_psnr(primary_image.get(), output_filename);
std::cout << "PSNR: " << std::setprecision(2) << std::fixed << psnr << " ";
diff --git a/libheif/api/libheif/heif_image_handle.cc b/libheif/api/libheif/heif_image_handle.cc
index eb4facc9..20bf9468 100644
--- a/libheif/api/libheif/heif_image_handle.cc
+++ b/libheif/api/libheif/heif_image_handle.cc
@@ -28,6 +28,7 @@
#include <cstring>
#include <string>
#include <memory>
+#include <vector>
void heif_image_handle_release(const heif_image_handle* handle)
diff --git a/libheif/file.cc b/libheif/file.cc
index a652ae6a..3800e83b 100644
--- a/libheif/file.cc
+++ b/libheif/file.cc
@@ -163,39 +163,59 @@ void HeifFile::new_empty_file()
}
-void HeifFile::init_for_image()
+void HeifFile::init_for_meta_item()
{
- if (m_meta_box) {
- return;
+ if (!m_meta_box) {
+ m_meta_box = std::make_shared<Box_meta>();
+ m_top_level_boxes.push_back(m_meta_box);
}
- m_hdlr_box = std::make_shared<Box_hdlr>();
- m_meta_box = std::make_shared<Box_meta>();
- m_ipco_box = std::make_shared<Box_ipco>();
- m_ipma_box = std::make_shared<Box_ipma>();
- m_iloc_box = std::make_shared<Box_iloc>();
- m_iinf_box = std::make_shared<Box_iinf>();
- m_iprp_box = std::make_shared<Box_iprp>();
- m_pitm_box = std::make_shared<Box_pitm>();
+ if (!m_hdlr_box) {
+ m_hdlr_box = std::make_shared<Box_hdlr>();
+ m_hdlr_box->set_handler_type(fourcc("null"));
+ m_meta_box->append_child_box(m_hdlr_box);
+ }
- m_meta_box->append_child_box(m_hdlr_box);
- m_meta_box->append_child_box(m_pitm_box);
- m_meta_box->append_child_box(m_iloc_box);
- m_meta_box->append_child_box(m_iinf_box);
- m_meta_box->append_child_box(m_iprp_box);
+ if (!m_iloc_box) {
+ m_iloc_box = std::make_shared<Box_iloc>();
+ m_meta_box->append_child_box(m_iloc_box);
+ }
- m_iprp_box->append_child_box(m_ipco_box);
- m_iprp_box->append_child_box(m_ipma_box);
+ if (!m_iinf_box) {
+ m_iinf_box = std::make_shared<Box_iinf>();
+ m_meta_box->append_child_box(m_iinf_box);
+ }
+}
- m_infe_boxes.clear();
- m_top_level_boxes.push_back(m_meta_box);
-#if ENABLE_EXPERIMENTAL_MINI_FORMAT
- // TODO: do not create 'mini' box as we cannot write them yet.
- // if we include it in the top_level_boxes, it will be written into every file.
- //m_mini_box = std::make_shared<Box_mini>();
- //m_top_level_boxes.push_back(m_mini_box);
-#endif
+void HeifFile::init_for_image()
+{
+ init_for_meta_item();
+
+ // Upgrade handler from "null" to "pict" if needed
+ if (m_hdlr_box->get_handler_type() == fourcc("null")) {
+ m_hdlr_box->set_handler_type(fourcc("pict"));
+ }
+
+ if (!m_pitm_box) {
+ m_pitm_box = std::make_shared<Box_pitm>();
+ m_meta_box->append_child_box(m_pitm_box);
+ }
+
+ if (!m_iprp_box) {
+ m_iprp_box = std::make_shared<Box_iprp>();
+ m_meta_box->append_child_box(m_iprp_box);
+ }
+
+ if (!m_ipco_box) {
+ m_ipco_box = std::make_shared<Box_ipco>();
+ m_iprp_box->append_child_box(m_ipco_box);
+ }
+
+ if (!m_ipma_box) {
+ m_ipma_box = std::make_shared<Box_ipma>();
+ m_iprp_box->append_child_box(m_ipma_box);
+ }
}
@@ -890,6 +910,28 @@ Result<std::shared_ptr<Box_infe>> HeifFile::add_new_infe_box(uint32_t item_type)
}
+Result<std::shared_ptr<Box_infe>> HeifFile::add_new_meta_infe_box(uint32_t item_type)
+{
+ init_for_meta_item();
+
+ auto idResult = get_unused_item_id();
+ if (!idResult) {
+ return idResult.error();
+ }
+ heif_item_id id = *idResult;
+
+ auto infe = std::make_shared<Box_infe>();
+ infe->set_item_ID(id);
+ infe->set_hidden_item(false);
+ infe->set_item_type_4cc(item_type);
+
+ m_infe_boxes[id] = infe;
+ m_iinf_box->append_child_box(infe);
+
+ return infe;
+}
+
+
void HeifFile::add_ispe_property(heif_item_id id, uint32_t width, uint32_t height, bool essential)
{
auto ispe = std::make_shared<Box_ispe>();
@@ -1013,7 +1055,7 @@ Result<heif_item_id> HeifFile::add_infe_mime(const char* content_type, heif_meta
{
// create an infe box describing what kind of data we are storing (this also creates a new ID)
- auto infe_result = add_new_infe_box(fourcc("mime"));
+ auto infe_result = add_new_meta_infe_box(fourcc("mime"));
if (!infe_result) {
return infe_result.error();
}
@@ -1033,7 +1075,7 @@ Result<heif_item_id> HeifFile::add_precompressed_infe_mime(const char* content_t
{
// create an infe box describing what kind of data we are storing (this also creates a new ID)
- auto infe_result = add_new_infe_box(fourcc("mime"));
+ auto infe_result = add_new_meta_infe_box(fourcc("mime"));
if (!infe_result) {
return infe_result.error();
}
@@ -1053,7 +1095,7 @@ Result<heif_item_id> HeifFile::add_infe_uri(const char* item_uri_type, const uin
{
// create an infe box describing what kind of data we are storing (this also creates a new ID)
- auto infe_result = add_new_infe_box(fourcc("uri "));
+ auto infe_result = add_new_meta_infe_box(fourcc("uri "));
if (!infe_result) {
return infe_result.error();
}
diff --git a/libheif/file.h b/libheif/file.h
index c9360ec9..a76bc962 100644
--- a/libheif/file.h
+++ b/libheif/file.h
@@ -82,6 +82,8 @@ public:
void new_empty_file();
+ void init_for_meta_item();
+
void init_for_image();
void init_for_sequence();
@@ -96,7 +98,7 @@ public:
int get_num_images() const { return static_cast<int>(m_infe_boxes.size()); }
- heif_item_id get_primary_image_ID() const { return m_pitm_box->get_item_ID(); }
+ heif_item_id get_primary_image_ID() const { return m_pitm_box ? m_pitm_box->get_item_ID() : 0; }
size_t get_number_of_items() const { return m_infe_boxes.size(); }
@@ -194,6 +196,8 @@ public:
Result<std::shared_ptr<Box_infe>> add_new_infe_box(uint32_t item_type);
+ Result<std::shared_ptr<Box_infe>> add_new_meta_infe_box(uint32_t item_type);
+
void add_ispe_property(heif_item_id id, uint32_t width, uint32_t height, bool essential);
// set irot/imir according to heif_orientation