Skip to content

Commit

Permalink
wip: new mini implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
bradh committed Oct 5, 2024
1 parent 5b76291 commit b0933c6
Show file tree
Hide file tree
Showing 18 changed files with 696 additions and 19 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,7 @@ option(WITH_REDUCED_VISIBILITY "Reduced symbol visibility in library" ON)
option(WITH_HEADER_COMPRESSION OFF)
option(ENABLE_MULTITHREADING_SUPPORT "Switch off for platforms without multithreading support" ON)
option(ENABLE_PARALLEL_TILE_DECODING "Will launch multiple decoders to decode tiles in parallel (requires ENABLE_MULTITHREADING_SUPPORT)" ON)
option(ENABLE_EXPERIMENTAL_MINI_FORMAT "Enable experimental (draft) low-overhead box format (likely reduced interoperability)." OFF)

if (WITH_REDUCED_VISIBILITY)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
Expand Down
2 changes: 2 additions & 0 deletions go/heif/heif.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,8 @@ const (

SuberrorNoAVCCBox = C.heif_suberror_No_avcC_box

SuberrorNoMiniBox = C.heif_suberror_No_mini_box

SuberrorInvalidCleanAperture = C.heif_suberror_Invalid_clean_aperture

// Invalid specification of overlay image
Expand Down
7 changes: 7 additions & 0 deletions libheif/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,13 @@ if (WITH_UNCOMPRESSED_CODEC)
codecs/uncompressed/decoder_tile_component_interleave.cc)
endif ()

if (ENABLE_EXPERIMENTAL_MINI_FORMAT)
target_compile_definitions(heif PUBLIC ENABLE_EXPERIMENTAL_MINI_FORMAT=1)
target_sources(heif PRIVATE
mini.h
mini.cc)
endif ()

write_basic_package_version_file(${PROJECT_NAME}-config-version.cmake COMPATIBILITY ExactVersion)

install(TARGETS heif EXPORT ${PROJECT_NAME}-config
Expand Down
59 changes: 44 additions & 15 deletions libheif/api/libheif/heif.cc
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ heif_error heif_has_compatible_filetype(const uint8_t* data, int len)
heif_brand2_miaf,
heif_brand2_mif1,
heif_brand2_mif2
#if ENABLE_EXPERIMENTAL_MINI_FORMAT
, heif_brand2_mif3
#endif
};

auto it = supported_brands.find(main_brand);
Expand Down Expand Up @@ -302,6 +305,16 @@ heif_brand2 heif_fourcc_to_brand(const char* fourcc_string)
}


heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len)
{
if (len < 16) {
return heif_unknown_brand;
}

return heif_fourcc_to_brand((char*) (data + 12));
}


void heif_brand_to_fourcc(heif_brand2 brand, char* out_fourcc)
{
if (out_fourcc) {
Expand Down Expand Up @@ -435,36 +448,52 @@ TriBool is_png(const uint8_t* data, int len)

const char* heif_get_file_mime_type(const uint8_t* data, int len)
{
heif_brand mainBrand = heif_main_brand(data, len);
heif_brand2 mainBrand = heif_read_main_brand(data, len);

if (mainBrand == heif_heic ||
mainBrand == heif_heix ||
mainBrand == heif_heim ||
mainBrand == heif_heis) {
if (mainBrand == heif_brand2_heic ||
mainBrand == heif_brand2_heix ||
mainBrand == heif_brand2_heim ||
mainBrand == heif_brand2_heis) {
return "image/heic";
}
else if (mainBrand == heif_mif1) {
else if (mainBrand == heif_brand2_mif1) {
return "image/heif";
}
else if (mainBrand == heif_hevc ||
mainBrand == heif_hevx ||
mainBrand == heif_hevm ||
mainBrand == heif_hevs) {
else if (mainBrand == heif_brand2_hevc ||
mainBrand == heif_brand2_hevx ||
mainBrand == heif_brand2_hevm ||
mainBrand == heif_brand2_hevs) {
return "image/heic-sequence";
}
else if (mainBrand == heif_msf1) {
else if (mainBrand == heif_brand2_msf1) {
return "image/heif-sequence";
}
else if (mainBrand == heif_avif) {
else if (mainBrand == heif_brand2_avif) {
return "image/avif";
}
else if (mainBrand == heif_avis) {
#if ENABLE_EXPERIMENTAL_MINI_FORMAT
else if (mainBrand == heif_brand2_mif3) {
heif_brand2 minorBrand = heif_read_minor_version_brand(data, len);
if (minorBrand == heif_brand2_avif) {
return "image/avif";
}
if (minorBrand == heif_brand2_heic ||
minorBrand == heif_brand2_heix ||
minorBrand == heif_brand2_heim ||
minorBrand == heif_brand2_heis) {
return "image/heic";
}
// There could be other options in here, like VVC or J2K
return "image/heif";
}
#endif
else if (mainBrand == heif_brand2_avis) {
return "image/avif-sequence";
}
else if (mainBrand == heif_j2ki) {
else if (mainBrand == heif_brand2_j2ki) {
return "image/hej2k";
}
else if (mainBrand == heif_j2is) {
else if (mainBrand == heif_brand2_j2is) {
return "image/j2is";
}
else if (is_jpeg(data, len) == TriBool::Yes) {
Expand Down
14 changes: 14 additions & 0 deletions libheif/api/libheif/heif.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ enum heif_suberror_code

heif_suberror_No_avcC_box = 143,

// we got a mini box, but could not read it properly
heif_suberror_No_mini_box = 149,

// Decompressing generic compression or header compression data failed (e.g. bitstream corruption)
heif_suberror_Decompression_invalid_data = 150,

Expand Down Expand Up @@ -781,6 +784,13 @@ typedef uint32_t heif_brand2;
*/
#define heif_brand2_mif2 heif_fourcc('m','i','f','2')

/**
* HEIF image structural brand (`mif3`).
*
* This indicates the low-overhead (ftyp+mini) structure.
*/
#define heif_brand2_mif3 heif_fourcc('m','i','f','3')

/**
* HEIF image sequence structural brand (`msf1`).
*
Expand Down Expand Up @@ -882,6 +892,10 @@ typedef uint32_t heif_brand2;
LIBHEIF_API
heif_brand2 heif_read_main_brand(const uint8_t* data, int len);

// input data should be at least 16 bytes
LIBHEIF_API
heif_brand2 heif_read_minor_version_brand(const uint8_t* data, int len);

// 'brand_fourcc' must be 4 character long, but need not be 0-terminated
LIBHEIF_API
heif_brand2 heif_fourcc_to_brand(const char* brand_fourcc);
Expand Down
1 change: 1 addition & 0 deletions libheif/api/libheif/heif_emscripten.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,7 @@ EMSCRIPTEN_BINDINGS(libheif) {
.value("heif_suberror_Missing_grid_images", heif_suberror_Missing_grid_images)
.value("heif_suberror_No_av1C_box", heif_suberror_No_av1C_box)
.value("heif_suberror_No_avcC_box", heif_suberror_No_avcC_box)
.value("heif_suberror_No_mini_box", heif_suberror_No_mini_box)
.value("heif_suberror_Invalid_clean_aperture", heif_suberror_Invalid_clean_aperture)
.value("heif_suberror_Invalid_overlay_data", heif_suberror_Invalid_overlay_data)
.value("heif_suberror_Overlay_image_outside_of_canvas", heif_suberror_Overlay_image_outside_of_canvas)
Expand Down
14 changes: 14 additions & 0 deletions libheif/bitstream.cc
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,20 @@ uint32_t BitReader::get_bits32(int n)
return static_cast<uint32_t>(get_bits(n));
}

bool BitReader::get_flag()
{
return (get_bits(1) == 0x01);
}

std::vector<uint8_t> BitReader::read_bytes(uint32_t n)
{
// TODO: this implementation isn't very efficient
std::vector<uint8_t> bytes;
for (uint32_t i = 0; i < n; i++) {
bytes.push_back(get_bits8(8));
}
return bytes;
}

int BitReader::get_bits_fast(int n)
{
Expand Down
21 changes: 21 additions & 0 deletions libheif/bitstream.h
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,15 @@ class BitReader

uint32_t get_bits32(int n);

/**
* Get a one-bit flag value.
*
* @returns true if the next bit value is 1, otherwise false
*/
bool get_flag();

std::vector<uint8_t> read_bytes(uint32_t n);

int get_bits_fast(int n);

int peek_bits(int n);
Expand All @@ -396,10 +405,22 @@ class BitReader
return ((int64_t) bytes_remaining) * 8 + nextbits_cnt;
}

void set_start_offset(uint64_t offset)
{
start_offset = offset;
}

uint64_t get_file_offset() const
{
return start_offset + (data_length - bytes_remaining - (nextbits_cnt / 8));
}


private:
const uint8_t* data;
int data_length;
int bytes_remaining;
uint64_t start_offset = 0;

uint64_t nextbits; // left-aligned bits
int nextbits_cnt;
Expand Down
19 changes: 16 additions & 3 deletions libheif/box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -672,6 +672,12 @@ Error Box::read(BitstreamRange& range, std::shared_ptr<Box>* result)
box = std::make_shared<Box_tilC>();
break;

#if ENABLE_EXPERIMENTAL_MINI_FORMAT
case fourcc("mini"):
box = std::make_shared<Box_mini>();
break;
#endif

case fourcc("mdat"):
// avoid generating a 'Box_other'
box = std::make_shared<Box>();
Expand Down Expand Up @@ -1033,7 +1039,7 @@ Error Box_ftyp::parse(BitstreamRange& range)
m_minor_version = range.read32();

uint64_t box_size = get_box_size();
if (box_size < 8 || box_size - 8 <= get_header_size()) {
if (box_size < 8 || box_size - 8 < get_header_size()) {
// Sanity check.
return Error(heif_error_Invalid_input,
heif_suberror_Invalid_box_size,
Expand Down Expand Up @@ -1066,8 +1072,15 @@ std::string Box_ftyp::dump(Indent& indent) const
sstr << BoxHeader::dump(indent);

sstr << indent << "major brand: " << fourcc_to_string(m_major_brand) << "\n"
<< indent << "minor version: " << m_minor_version << "\n"
<< indent << "compatible brands: ";
<< indent << "minor version: ";
if (m_minor_version < ('A' << 24)) {
// This is probably a version number
sstr << m_minor_version;
} else {
// probably a 4CC, as used for mif3
sstr << fourcc_to_string(m_minor_version);
}
sstr << "\n" << indent << "compatible brands: ";

bool first = true;
for (uint32_t brand : m_compatible_brands) {
Expand Down
6 changes: 6 additions & 0 deletions libheif/box.h
Original file line number Diff line number Diff line change
Expand Up @@ -385,8 +385,12 @@ class Box_ftyp : public Box

std::vector<uint32_t> list_brands() const { return m_compatible_brands; }

uint32_t get_major_brand() const { return m_major_brand; }

void set_major_brand(heif_brand2 major_brand) { m_major_brand = major_brand; }

uint32_t get_minor_version() const { return m_minor_version; }

void set_minor_version(uint32_t minor_version) { m_minor_version = minor_version; }

void clear_compatible_brands() { m_compatible_brands.clear(); }
Expand Down Expand Up @@ -548,6 +552,8 @@ class Box_iloc : public FullBox

Error write_mdat_after_iloc(StreamWriter& writer);

void append_item(Item &item) { m_items.push_back(item); }

protected:
Error parse(BitstreamRange& range) override;

Expand Down
4 changes: 4 additions & 0 deletions libheif/codecs/avif_boxes.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@

class Box_av1C : public Box
{

// allow access to protected parse() method
friend class HeifFile;

public:
Box_av1C()
{
Expand Down
2 changes: 2 additions & 0 deletions libheif/error.cc
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ const char* Error::get_error_string(heif_suberror_code err)
return "Invalid data in generic compression inflation";
case heif_suberror_No_icbr_box:
return "No 'icbr' box";
case heif_suberror_No_mini_box:
return "No 'mini' box";


// --- Memory_allocation_error ---
Expand Down
Loading

0 comments on commit b0933c6

Please sign in to comment.