Commit 5219a32f for libheif
commit 5219a32f00030a4b9f1cf7b9095051a51bc443c5
Author: Dirk Farin <dirk.farin@gmail.com>
Date: Sun Jan 11 16:00:41 2026 +0100
replace 'sample' to 'chunk' in names of 'saio' box and add check for valid input (#1661)
diff --git a/libheif/sequences/seq_boxes.cc b/libheif/sequences/seq_boxes.cc
index 5f3ff1ac..91a050b8 100644
--- a/libheif/sequences/seq_boxes.cc
+++ b/libheif/sequences/seq_boxes.cc
@@ -1963,24 +1963,24 @@ void Box_saio::set_aux_info_type(uint32_t aux_info_type, uint32_t aux_info_type_
}
-void Box_saio::add_sample_offset(uint64_t s)
+void Box_saio::add_chunk_offset(uint64_t s)
{
if (s > 0xFFFFFFFF) {
m_need_64bit = true;
set_version(1);
}
- m_sample_offset.push_back(s);
+ m_chunk_offset.push_back(s);
}
-uint64_t Box_saio::get_sample_offset(uint32_t idx) const
+uint64_t Box_saio::get_chunk_offset(uint32_t idx) const
{
- if (idx >= m_sample_offset.size()) {
+ if (idx >= m_chunk_offset.size()) {
return 0;
}
else {
- return m_sample_offset[idx];
+ return m_chunk_offset[idx];
}
}
@@ -2006,8 +2006,8 @@ std::string Box_saio::dump(Indent& indent) const
sstr << fourcc_to_string(m_aux_info_type_parameter) << "\n";
}
- for (size_t i = 0; i < m_sample_offset.size(); i++) {
- sstr << indent << "[" << i << "] : 0x" << std::hex << m_sample_offset[i] << "\n";
+ for (size_t i = 0; i < m_chunk_offset.size(); i++) {
+ sstr << indent << "[" << i << "] : 0x" << std::hex << m_chunk_offset[i] << "\n";
}
return sstr.str();
@@ -2020,7 +2020,7 @@ void Box_saio::patch_file_pointers(StreamWriter& writer, size_t offset)
writer.set_position(m_offset_start_pos);
- for (uint64_t ptr : m_sample_offset) {
+ for (uint64_t ptr : m_chunk_offset) {
if (get_version() == 0 && ptr + offset > std::numeric_limits<uint32_t>::max()) {
writer.write32(0); // TODO: error
} else if (get_version() == 0) {
@@ -2043,16 +2043,16 @@ Error Box_saio::write(StreamWriter& writer) const
writer.write32(m_aux_info_type_parameter);
}
- if (m_sample_offset.size() > std::numeric_limits<uint32_t>::max()) {
+ if (m_chunk_offset.size() > std::numeric_limits<uint32_t>::max()) {
return Error{heif_error_Unsupported_feature,
heif_suberror_Unspecified,
- "Maximum number of samples exceeded"};
+ "Maximum number of chunks exceeded"};
}
- writer.write32(static_cast<uint32_t>(m_sample_offset.size()));
+ writer.write32(static_cast<uint32_t>(m_chunk_offset.size()));
m_offset_start_pos = writer.get_position();
- for (uint64_t size : m_sample_offset) {
+ for (uint64_t size : m_chunk_offset) {
if (m_need_64bit) {
writer.write64(size);
} else {
@@ -2075,26 +2075,28 @@ Error Box_saio::parse(BitstreamRange& range, const heif_security_limits* limits)
m_aux_info_type_parameter = range.read32();
}
- uint32_t num_samples = range.read32();
+ uint32_t num_chunks = range.read32();
- if (limits && num_samples > limits->max_sequence_frames) {
+ // We have no explicit maximum on the number of chunks.
+ // Use the maximum number of frames as an upper limit.
+ if (limits && num_chunks > limits->max_sequence_frames) {
return {
heif_error_Memory_allocation_error,
heif_suberror_Security_limit_exceeded,
- "Number of 'saio' samples exceeds the maximum number of sequence frames."
+ "Number of 'saio' chunks exceeds the maximum number of sequence frames."
};
}
// check required memory
- uint64_t mem_size = num_samples * sizeof(uint64_t);
+ uint64_t mem_size = num_chunks * sizeof(uint64_t);
if (auto err = m_memory_handle.alloc(mem_size, limits, "the 'saio' table")) {
return err;
}
- m_sample_offset.resize(num_samples);
+ m_chunk_offset.resize(num_chunks);
- for (uint32_t i = 0; i < num_samples; i++) {
+ for (uint32_t i = 0; i < num_chunks; i++) {
uint64_t offset;
if (get_version() == 1) {
offset = range.read64();
@@ -2103,7 +2105,7 @@ Error Box_saio::parse(BitstreamRange& range, const heif_security_limits* limits)
offset = range.read32();
}
- m_sample_offset[i] = offset;
+ m_chunk_offset[i] = offset;
if (range.error()) {
return range.get_error();
diff --git a/libheif/sequences/seq_boxes.h b/libheif/sequences/seq_boxes.h
index b7154d80..7c29ac4c 100644
--- a/libheif/sequences/seq_boxes.h
+++ b/libheif/sequences/seq_boxes.h
@@ -466,6 +466,8 @@ public:
const std::vector<uint32_t>& get_offsets() const { return m_offsets; }
+ size_t get_number_of_chunks() const { return m_offsets.size(); }
+
void patch_file_pointers(StreamWriter&, size_t offset) override;
protected:
@@ -876,12 +878,12 @@ public:
uint32_t get_aux_info_type_parameter() const { return m_aux_info_type_parameter; }
- void add_sample_offset(uint64_t offset);
+ void add_chunk_offset(uint64_t offset);
- // This will be 1 if all infos are written contiguously
- size_t get_num_samples() const { return m_sample_offset.size(); }
+ // If this is 1, the SAI data of all samples is written contiguously in the file.
+ size_t get_num_chunks() const { return m_chunk_offset.size(); }
- uint64_t get_sample_offset(uint32_t idx) const;
+ uint64_t get_chunk_offset(uint32_t idx) const;
std::string dump(Indent&) const override;
@@ -901,8 +903,8 @@ private:
bool m_need_64bit = false;
mutable uint64_t m_offset_start_pos;
- // If sample_offset==1, all samples are stored contiguous in the file
- std::vector<uint64_t> m_sample_offset;
+ // If |chunk_offset|==1, the SAI data of all samples is stored contiguously in the file
+ std::vector<uint64_t> m_chunk_offset;
MemoryHandle m_memory_handle;
};
diff --git a/libheif/sequences/track.cc b/libheif/sequences/track.cc
index 669ae298..35759678 100644
--- a/libheif/sequences/track.cc
+++ b/libheif/sequences/track.cc
@@ -96,7 +96,7 @@ void SampleAuxInfoHelper::write_interleaved(const std::shared_ptr<HeifFile>& fil
if (m_interleaved && !m_data.empty()) {
// TODO: I think this does not work because the image data does not know that there is SAI in-between
uint64_t pos = file->append_mdat_data(m_data);
- m_saio->add_sample_offset(pos);
+ m_saio->add_chunk_offset(pos);
m_data.clear();
}
@@ -109,7 +109,7 @@ void SampleAuxInfoHelper::write_all(const std::shared_ptr<Box>& parent, const st
if (!m_data.empty()) {
uint64_t pos = file->append_mdat_data(m_data);
- m_saio->add_sample_offset(pos);
+ m_saio->add_chunk_offset(pos);
}
}
@@ -120,9 +120,9 @@ SampleAuxInfoReader::SampleAuxInfoReader(std::shared_ptr<Box_saiz> saiz,
m_saiz = saiz;
m_saio = saio;
- m_contiguous = (saio->get_num_samples() == 1);
+ m_contiguous = (saio->get_num_chunks() == 1);
if (m_contiguous) {
- uint64_t offset = saio->get_sample_offset(0);
+ uint64_t offset = saio->get_chunk_offset(0);
auto nSamples = saiz->get_num_samples();
for (uint32_t i = 0; i < nSamples; i++) {
@@ -151,7 +151,7 @@ Result<std::vector<uint8_t> > SampleAuxInfoReader::get_sample_info(const HeifFil
offset = m_contiguous_offsets[idx];
}
else {
- offset = m_saio->get_sample_offset(idx);
+ offset = m_saio->get_chunk_offset(idx);
}
uint8_t size = m_saiz->get_sample_size(idx);
@@ -409,6 +409,15 @@ Error Track::load(const std::shared_ptr<Box_trak>& trak_box)
}
if (saio) {
+ if (saio->get_num_chunks() != 1 &&
+ saio->get_num_chunks() != m_stco->get_number_of_chunks()) {
+ return Error{
+ heif_error_Invalid_input,
+ heif_suberror_Unspecified,
+ "Invalid number of chunks in 'saio' box."
+ };
+ }
+
if (aux_info_type == fourcc("suid")) {
m_aux_reader_content_ids = std::make_unique<SampleAuxInfoReader>(saiz, saio);
}