From ae75fade657888a5c8570346e086255a72b0ac65 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sun, 9 Jul 2023 21:07:00 +1000 Subject: [PATCH 1/5] move hvcC box into HEVC specific file --- libheif/box.cc | 285 +---------------------------------------------- libheif/box.h | 67 ----------- libheif/file.h | 1 + libheif/hevc.cc | 288 ++++++++++++++++++++++++++++++++++++++++++++++++ libheif/hevc.h | 67 +++++++++++ 5 files changed, 357 insertions(+), 351 deletions(-) diff --git a/libheif/box.cc b/libheif/box.cc index f86199d42d..b7679611ac 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -24,6 +24,7 @@ #include "security_limits.h" #include "nclx.h" #include "jpeg2000.h" +#include "hevc.h" #include "mask_image.h" #include @@ -3043,290 +3044,6 @@ void Box_iref::add_reference(heif_item_id from_id, uint32_t type, const std::vec } -Error Box_hvcC::parse(BitstreamRange& range) -{ - //parse_full_box_header(range); - - uint8_t byte; - - auto& c = m_configuration; // abbreviation - - c.configuration_version = range.read8(); - byte = range.read8(); - c.general_profile_space = (byte >> 6) & 3; - c.general_tier_flag = (byte >> 5) & 1; - c.general_profile_idc = (byte & 0x1F); - - c.general_profile_compatibility_flags = range.read32(); - - for (int i = 0; i < 6; i++) { - byte = range.read8(); - - for (int b = 0; b < 8; b++) { - c.general_constraint_indicator_flags[i * 8 + b] = (byte >> (7 - b)) & 1; - } - } - - c.general_level_idc = range.read8(); - c.min_spatial_segmentation_idc = range.read16() & 0x0FFF; - c.parallelism_type = range.read8() & 0x03; - c.chroma_format = range.read8() & 0x03; - c.bit_depth_luma = static_cast((range.read8() & 0x07) + 8); - c.bit_depth_chroma = static_cast((range.read8() & 0x07) + 8); - c.avg_frame_rate = range.read16(); - - byte = range.read8(); - c.constant_frame_rate = (byte >> 6) & 0x03; - c.num_temporal_layers = (byte >> 3) & 0x07; - c.temporal_id_nested = (byte >> 2) & 1; - - m_length_size = static_cast((byte & 0x03) + 1); - - int nArrays = range.read8(); - - for (int i = 0; i < nArrays && !range.error(); i++) { - byte = range.read8(); - - NalArray array; - - array.m_array_completeness = (byte >> 6) & 1; - array.m_NAL_unit_type = (byte & 0x3F); - - int nUnits = range.read16(); - for (int u = 0; u < nUnits && !range.error(); u++) { - - std::vector nal_unit; - int size = range.read16(); - if (!size) { - // Ignore empty NAL units. - continue; - } - - if (range.prepare_read(size)) { - nal_unit.resize(size); - bool success = range.get_istream()->read((char*) nal_unit.data(), size); - assert(success); - (void) success; - } - - array.m_nal_units.push_back(std::move(nal_unit)); - } - - m_nal_array.push_back(std::move(array)); - } - - range.skip_to_end_of_box(); - - return range.get_error(); -} - - -std::string Box_hvcC::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << Box::dump(indent); - - const auto& c = m_configuration; // abbreviation - - sstr << indent << "configuration_version: " << ((int) c.configuration_version) << "\n" - << indent << "general_profile_space: " << ((int) c.general_profile_space) << "\n" - << indent << "general_tier_flag: " << c.general_tier_flag << "\n" - << indent << "general_profile_idc: " << ((int) c.general_profile_idc) << "\n"; - - sstr << indent << "general_profile_compatibility_flags: "; - for (int i = 0; i < 32; i++) { - sstr << ((c.general_profile_compatibility_flags >> (31 - i)) & 1); - if ((i % 8) == 7) sstr << ' '; - else if ((i % 4) == 3) sstr << '.'; - } - sstr << "\n"; - - sstr << indent << "general_constraint_indicator_flags: "; - int cnt = 0; - for (int i = 0; i < configuration::NUM_CONSTRAINT_INDICATOR_FLAGS; i++) { - bool b = c.general_constraint_indicator_flags[i]; - - sstr << (b ? 1 : 0); - cnt++; - if ((cnt % 8) == 0) - sstr << ' '; - } - sstr << "\n"; - - sstr << indent << "general_level_idc: " << ((int) c.general_level_idc) << "\n" - << indent << "min_spatial_segmentation_idc: " << c.min_spatial_segmentation_idc << "\n" - << indent << "parallelism_type: " << ((int) c.parallelism_type) << "\n" - << indent << "chroma_format: "; - - switch (c.chroma_format) { - case 1: - sstr << "4:2:0"; - break; - case 2: - sstr << "4:2:2"; - break; - case 3: - sstr << "4:4:4"; - break; - default: - sstr << ((int) c.chroma_format); - break; - } - - sstr << "\n" - << indent << "bit_depth_luma: " << ((int) c.bit_depth_luma) << "\n" - << indent << "bit_depth_chroma: " << ((int) c.bit_depth_chroma) << "\n" - << indent << "avg_frame_rate: " << c.avg_frame_rate << "\n" - << indent << "constant_frame_rate: " << ((int) c.constant_frame_rate) << "\n" - << indent << "num_temporal_layers: " << ((int) c.num_temporal_layers) << "\n" - << indent << "temporal_id_nested: " << ((int) c.temporal_id_nested) << "\n" - << indent << "length_size: " << ((int) m_length_size) << "\n"; - - for (const auto& array : m_nal_array) { - sstr << indent << "\n"; - - indent++; - sstr << indent << "array_completeness: " << ((int) array.m_array_completeness) << "\n" - << indent << "NAL_unit_type: " << ((int) array.m_NAL_unit_type) << "\n"; - - for (const auto& unit : array.m_nal_units) { - //sstr << " unit with " << unit.size() << " bytes of data\n"; - sstr << indent; - for (uint8_t b : unit) { - sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " "; - } - sstr << "\n"; - sstr << std::dec; - } - - indent--; - } - - return sstr.str(); -} - - -bool Box_hvcC::get_headers(std::vector* dest) const -{ - for (const auto& array : m_nal_array) { - for (const auto& unit : array.m_nal_units) { - - dest->push_back((unit.size() >> 24) & 0xFF); - dest->push_back((unit.size() >> 16) & 0xFF); - dest->push_back((unit.size() >> 8) & 0xFF); - dest->push_back((unit.size() >> 0) & 0xFF); - - /* - dest->push_back(0); - dest->push_back(0); - dest->push_back(1); - */ - - dest->insert(dest->end(), unit.begin(), unit.end()); - } - } - - return true; -} - - -void Box_hvcC::append_nal_data(const std::vector& nal) -{ - NalArray array; - array.m_array_completeness = 0; - array.m_NAL_unit_type = uint8_t(nal[0] >> 1); - array.m_nal_units.push_back(nal); - - m_nal_array.push_back(array); -} - -void Box_hvcC::append_nal_data(const uint8_t* data, size_t size) -{ - std::vector nal; - nal.resize(size); - memcpy(nal.data(), data, size); - - NalArray array; - array.m_array_completeness = 0; - array.m_NAL_unit_type = uint8_t(nal[0] >> 1); - array.m_nal_units.push_back(std::move(nal)); - - m_nal_array.push_back(array); -} - - -Error Box_hvcC::write(StreamWriter& writer) const -{ - size_t box_start = reserve_box_header_space(writer); - - const auto& c = m_configuration; // abbreviation - - writer.write8(c.configuration_version); - - writer.write8((uint8_t) (((c.general_profile_space & 3) << 6) | - ((c.general_tier_flag & 1) << 5) | - (c.general_profile_idc & 0x1F))); - - writer.write32(c.general_profile_compatibility_flags); - - for (int i = 0; i < 6; i++) { - uint8_t byte = 0; - - for (int b = 0; b < 8; b++) { - if (c.general_constraint_indicator_flags[i * 8 + b]) { - byte |= 1; - } - - byte = (uint8_t) (byte << 1); - } - - writer.write8(byte); - } - - writer.write8(c.general_level_idc); - writer.write16((c.min_spatial_segmentation_idc & 0x0FFF) | 0xF000); - writer.write8(c.parallelism_type | 0xFC); - writer.write8(c.chroma_format | 0xFC); - writer.write8((uint8_t) ((c.bit_depth_luma - 8) | 0xF8)); - writer.write8((uint8_t) ((c.bit_depth_chroma - 8) | 0xF8)); - writer.write16(c.avg_frame_rate); - - writer.write8((uint8_t) (((c.constant_frame_rate & 0x03) << 6) | - ((c.num_temporal_layers & 0x07) << 3) | - ((c.temporal_id_nested & 1) << 2) | - ((m_length_size - 1) & 0x03))); - - size_t nArrays = m_nal_array.size(); - if (nArrays > 0xFF) { - // TODO: error: too many NAL units - } - - writer.write8((uint8_t) nArrays); - - for (const NalArray& array : m_nal_array) { - - writer.write8((uint8_t) (((array.m_array_completeness & 1) << 6) | - (array.m_NAL_unit_type & 0x3F))); - - size_t nUnits = array.m_nal_units.size(); - if (nUnits > 0xFFFF) { - // TODO: error: too many NAL units - } - - writer.write16((uint16_t) nUnits); - - for (const std::vector& nal_unit : array.m_nal_units) { - writer.write16((uint16_t) nal_unit.size()); - writer.write(nal_unit); - } - } - - prepend_header(writer, box_start); - - return Error::Ok; -} - - Error Box_av1C::parse(BitstreamRange& range) { //parse_full_box_header(range); diff --git a/libheif/box.h b/libheif/box.h index d85261f320..6de6b4ad6d 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -770,73 +770,6 @@ class Box_iref : public FullBox }; -class Box_hvcC : public Box -{ -public: - Box_hvcC() - { - set_short_type(fourcc("hvcC")); - } - - struct configuration - { - uint8_t configuration_version; - uint8_t general_profile_space; - bool general_tier_flag; - uint8_t general_profile_idc; - uint32_t general_profile_compatibility_flags; - - static const int NUM_CONSTRAINT_INDICATOR_FLAGS = 48; - std::bitset general_constraint_indicator_flags; - - uint8_t general_level_idc; - - uint16_t min_spatial_segmentation_idc; - uint8_t parallelism_type; - uint8_t chroma_format; - uint8_t bit_depth_luma; - uint8_t bit_depth_chroma; - uint16_t avg_frame_rate; - - uint8_t constant_frame_rate; - uint8_t num_temporal_layers; - uint8_t temporal_id_nested; - }; - - - std::string dump(Indent&) const override; - - bool get_headers(std::vector* dest) const; - - void set_configuration(const configuration& config) { m_configuration = config; } - - const configuration& get_configuration() const { return m_configuration; } - - void append_nal_data(const std::vector& nal); - - void append_nal_data(const uint8_t* data, size_t size); - - Error write(StreamWriter& writer) const override; - -protected: - Error parse(BitstreamRange& range) override; - -private: - struct NalArray - { - uint8_t m_array_completeness; - uint8_t m_NAL_unit_type; - - std::vector > m_nal_units; - }; - - configuration m_configuration; - uint8_t m_length_size = 4; // default: 4 bytes for NAL unit lengths - - std::vector m_nal_array; -}; - - class Box_av1C : public Box { public: diff --git a/libheif/file.h b/libheif/file.h index 2a8cd67cea..9b6e839b8b 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -22,6 +22,7 @@ #define LIBHEIF_FILE_H #include "box.h" +#include "hevc.h" #include #include diff --git a/libheif/hevc.cc b/libheif/hevc.cc index 651d6de7db..727a1088b8 100644 --- a/libheif/hevc.cc +++ b/libheif/hevc.cc @@ -21,7 +21,295 @@ #include "hevc.h" #include "bitstream.h" +#include #include +#include +#include +#include +#include + +Error Box_hvcC::parse(BitstreamRange& range) +{ + //parse_full_box_header(range); + + uint8_t byte; + + auto& c = m_configuration; // abbreviation + + c.configuration_version = range.read8(); + byte = range.read8(); + c.general_profile_space = (byte >> 6) & 3; + c.general_tier_flag = (byte >> 5) & 1; + c.general_profile_idc = (byte & 0x1F); + + c.general_profile_compatibility_flags = range.read32(); + + for (int i = 0; i < 6; i++) { + byte = range.read8(); + + for (int b = 0; b < 8; b++) { + c.general_constraint_indicator_flags[i * 8 + b] = (byte >> (7 - b)) & 1; + } + } + + c.general_level_idc = range.read8(); + c.min_spatial_segmentation_idc = range.read16() & 0x0FFF; + c.parallelism_type = range.read8() & 0x03; + c.chroma_format = range.read8() & 0x03; + c.bit_depth_luma = static_cast((range.read8() & 0x07) + 8); + c.bit_depth_chroma = static_cast((range.read8() & 0x07) + 8); + c.avg_frame_rate = range.read16(); + + byte = range.read8(); + c.constant_frame_rate = (byte >> 6) & 0x03; + c.num_temporal_layers = (byte >> 3) & 0x07; + c.temporal_id_nested = (byte >> 2) & 1; + + m_length_size = static_cast((byte & 0x03) + 1); + + int nArrays = range.read8(); + + for (int i = 0; i < nArrays && !range.error(); i++) { + byte = range.read8(); + + NalArray array; + + array.m_array_completeness = (byte >> 6) & 1; + array.m_NAL_unit_type = (byte & 0x3F); + + int nUnits = range.read16(); + for (int u = 0; u < nUnits && !range.error(); u++) { + + std::vector nal_unit; + int size = range.read16(); + if (!size) { + // Ignore empty NAL units. + continue; + } + + if (range.prepare_read(size)) { + nal_unit.resize(size); + bool success = range.get_istream()->read((char*) nal_unit.data(), size); + assert(success); + (void) success; + } + + array.m_nal_units.push_back(std::move(nal_unit)); + } + + m_nal_array.push_back(std::move(array)); + } + + range.skip_to_end_of_box(); + + return range.get_error(); +} + + +std::string Box_hvcC::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + + const auto& c = m_configuration; // abbreviation + + sstr << indent << "configuration_version: " << ((int) c.configuration_version) << "\n" + << indent << "general_profile_space: " << ((int) c.general_profile_space) << "\n" + << indent << "general_tier_flag: " << c.general_tier_flag << "\n" + << indent << "general_profile_idc: " << ((int) c.general_profile_idc) << "\n"; + + sstr << indent << "general_profile_compatibility_flags: "; + for (int i = 0; i < 32; i++) { + sstr << ((c.general_profile_compatibility_flags >> (31 - i)) & 1); + if ((i % 8) == 7) sstr << ' '; + else if ((i % 4) == 3) sstr << '.'; + } + sstr << "\n"; + + sstr << indent << "general_constraint_indicator_flags: "; + int cnt = 0; + for (int i = 0; i < configuration::NUM_CONSTRAINT_INDICATOR_FLAGS; i++) { + bool b = c.general_constraint_indicator_flags[i]; + + sstr << (b ? 1 : 0); + cnt++; + if ((cnt % 8) == 0) + sstr << ' '; + } + sstr << "\n"; + + sstr << indent << "general_level_idc: " << ((int) c.general_level_idc) << "\n" + << indent << "min_spatial_segmentation_idc: " << c.min_spatial_segmentation_idc << "\n" + << indent << "parallelism_type: " << ((int) c.parallelism_type) << "\n" + << indent << "chroma_format: "; + + switch (c.chroma_format) { + case 1: + sstr << "4:2:0"; + break; + case 2: + sstr << "4:2:2"; + break; + case 3: + sstr << "4:4:4"; + break; + default: + sstr << ((int) c.chroma_format); + break; + } + + sstr << "\n" + << indent << "bit_depth_luma: " << ((int) c.bit_depth_luma) << "\n" + << indent << "bit_depth_chroma: " << ((int) c.bit_depth_chroma) << "\n" + << indent << "avg_frame_rate: " << c.avg_frame_rate << "\n" + << indent << "constant_frame_rate: " << ((int) c.constant_frame_rate) << "\n" + << indent << "num_temporal_layers: " << ((int) c.num_temporal_layers) << "\n" + << indent << "temporal_id_nested: " << ((int) c.temporal_id_nested) << "\n" + << indent << "length_size: " << ((int) m_length_size) << "\n"; + + for (const auto& array : m_nal_array) { + sstr << indent << "\n"; + + indent++; + sstr << indent << "array_completeness: " << ((int) array.m_array_completeness) << "\n" + << indent << "NAL_unit_type: " << ((int) array.m_NAL_unit_type) << "\n"; + + for (const auto& unit : array.m_nal_units) { + //sstr << " unit with " << unit.size() << " bytes of data\n"; + sstr << indent; + for (uint8_t b : unit) { + sstr << std::setfill('0') << std::setw(2) << std::hex << ((int) b) << " "; + } + sstr << "\n"; + sstr << std::dec; + } + + indent--; + } + + return sstr.str(); +} + + +bool Box_hvcC::get_headers(std::vector* dest) const +{ + for (const auto& array : m_nal_array) { + for (const auto& unit : array.m_nal_units) { + + dest->push_back((unit.size() >> 24) & 0xFF); + dest->push_back((unit.size() >> 16) & 0xFF); + dest->push_back((unit.size() >> 8) & 0xFF); + dest->push_back((unit.size() >> 0) & 0xFF); + + /* + dest->push_back(0); + dest->push_back(0); + dest->push_back(1); + */ + + dest->insert(dest->end(), unit.begin(), unit.end()); + } + } + + return true; +} + + +void Box_hvcC::append_nal_data(const std::vector& nal) +{ + NalArray array; + array.m_array_completeness = 0; + array.m_NAL_unit_type = uint8_t(nal[0] >> 1); + array.m_nal_units.push_back(nal); + + m_nal_array.push_back(array); +} + +void Box_hvcC::append_nal_data(const uint8_t* data, size_t size) +{ + std::vector nal; + nal.resize(size); + memcpy(nal.data(), data, size); + + NalArray array; + array.m_array_completeness = 0; + array.m_NAL_unit_type = uint8_t(nal[0] >> 1); + array.m_nal_units.push_back(std::move(nal)); + + m_nal_array.push_back(array); +} + + +Error Box_hvcC::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + const auto& c = m_configuration; // abbreviation + + writer.write8(c.configuration_version); + + writer.write8((uint8_t) (((c.general_profile_space & 3) << 6) | + ((c.general_tier_flag & 1) << 5) | + (c.general_profile_idc & 0x1F))); + + writer.write32(c.general_profile_compatibility_flags); + + for (int i = 0; i < 6; i++) { + uint8_t byte = 0; + + for (int b = 0; b < 8; b++) { + if (c.general_constraint_indicator_flags[i * 8 + b]) { + byte |= 1; + } + + byte = (uint8_t) (byte << 1); + } + + writer.write8(byte); + } + + writer.write8(c.general_level_idc); + writer.write16((c.min_spatial_segmentation_idc & 0x0FFF) | 0xF000); + writer.write8(c.parallelism_type | 0xFC); + writer.write8(c.chroma_format | 0xFC); + writer.write8((uint8_t) ((c.bit_depth_luma - 8) | 0xF8)); + writer.write8((uint8_t) ((c.bit_depth_chroma - 8) | 0xF8)); + writer.write16(c.avg_frame_rate); + + writer.write8((uint8_t) (((c.constant_frame_rate & 0x03) << 6) | + ((c.num_temporal_layers & 0x07) << 3) | + ((c.temporal_id_nested & 1) << 2) | + ((m_length_size - 1) & 0x03))); + + size_t nArrays = m_nal_array.size(); + if (nArrays > 0xFF) { + // TODO: error: too many NAL units + } + + writer.write8((uint8_t) nArrays); + + for (const NalArray& array : m_nal_array) { + + writer.write8((uint8_t) (((array.m_array_completeness & 1) << 6) | + (array.m_NAL_unit_type & 0x3F))); + + size_t nUnits = array.m_nal_units.size(); + if (nUnits > 0xFFFF) { + // TODO: error: too many NAL units + } + + writer.write16((uint16_t) nUnits); + + for (const std::vector& nal_unit : array.m_nal_units) { + writer.write16((uint16_t) nal_unit.size()); + writer.write(nal_unit); + } + } + + prepend_header(writer, box_start); + + return Error::Ok; +} static double read_depth_rep_info_element(BitReader& reader) diff --git a/libheif/hevc.h b/libheif/hevc.h index a067d2d129..6ba24aae43 100644 --- a/libheif/hevc.h +++ b/libheif/hevc.h @@ -26,9 +26,76 @@ #include "error.h" #include +#include #include + class Box_hvcC : public Box + { + public: + Box_hvcC() + { + set_short_type(fourcc("hvcC")); + } + + struct configuration + { + uint8_t configuration_version; + uint8_t general_profile_space; + bool general_tier_flag; + uint8_t general_profile_idc; + uint32_t general_profile_compatibility_flags; + + static const int NUM_CONSTRAINT_INDICATOR_FLAGS = 48; + std::bitset general_constraint_indicator_flags; + + uint8_t general_level_idc; + + uint16_t min_spatial_segmentation_idc; + uint8_t parallelism_type; + uint8_t chroma_format; + uint8_t bit_depth_luma; + uint8_t bit_depth_chroma; + uint16_t avg_frame_rate; + + uint8_t constant_frame_rate; + uint8_t num_temporal_layers; + uint8_t temporal_id_nested; + }; + + + std::string dump(Indent&) const override; + + bool get_headers(std::vector* dest) const; + + void set_configuration(const configuration& config) { m_configuration = config; } + + const configuration& get_configuration() const { return m_configuration; } + + void append_nal_data(const std::vector& nal); + + void append_nal_data(const uint8_t* data, size_t size); + + Error write(StreamWriter& writer) const override; + + protected: + Error parse(BitstreamRange& range) override; + + private: + struct NalArray + { + uint8_t m_array_completeness; + uint8_t m_NAL_unit_type; + + std::vector > m_nal_units; + }; + + configuration m_configuration; + uint8_t m_length_size = 4; // default: 4 bytes for NAL unit lengths + + std::vector m_nal_array; + }; + class SEIMessage { public: From 526104ed581e44e4397b8d4c065c85ab8e95f15f Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Sun, 9 Jul 2023 21:11:28 +1000 Subject: [PATCH 2/5] move avcC/a1op/a1lx box handling into AVIF specific file --- libheif/avif.cc | 190 ++++++++++++++++++++++++++++++++++++++++++++++++ libheif/avif.h | 113 ++++++++++++++++++++++++++++ libheif/box.cc | 188 ----------------------------------------------- libheif/box.h | 110 ---------------------------- libheif/file.h | 1 + 5 files changed, 304 insertions(+), 298 deletions(-) diff --git a/libheif/avif.cc b/libheif/avif.cc index 2388317b51..36a3ee4050 100644 --- a/libheif/avif.cc +++ b/libheif/avif.cc @@ -22,11 +22,120 @@ #include "avif.h" #include "bitstream.h" #include "common_utils.h" +#include #include +#include // https://aomediacodec.github.io/av1-spec/av1-spec.pdf +Error Box_av1C::parse(BitstreamRange& range) +{ + //parse_full_box_header(range); + + uint8_t byte; + + auto& c = m_configuration; // abbreviation + + byte = range.read8(); + if ((byte & 0x80) == 0) { + // error: marker bit not set + } + + c.version = byte & 0x7F; + + byte = range.read8(); + c.seq_profile = (byte >> 5) & 0x7; + c.seq_level_idx_0 = byte & 0x1f; + + byte = range.read8(); + c.seq_tier_0 = (byte >> 7) & 1; + c.high_bitdepth = (byte >> 6) & 1; + c.twelve_bit = (byte >> 5) & 1; + c.monochrome = (byte >> 4) & 1; + c.chroma_subsampling_x = (byte >> 3) & 1; + c.chroma_subsampling_y = (byte >> 2) & 1; + c.chroma_sample_position = byte & 3; + + byte = range.read8(); + c.initial_presentation_delay_present = (byte >> 4) & 1; + if (c.initial_presentation_delay_present) { + c.initial_presentation_delay_minus_one = byte & 0x0F; + } + + const size_t configOBUs_bytes = range.get_remaining_bytes(); + m_config_OBUs.resize(configOBUs_bytes); + + if (!range.read(m_config_OBUs.data(), configOBUs_bytes)) { + // error + } + + return range.get_error(); +} + + +Error Box_av1C::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + const auto& c = m_configuration; // abbreviation + + writer.write8(c.version | 0x80); + + writer.write8((uint8_t) (((c.seq_profile & 0x7) << 5) | + (c.seq_level_idx_0 & 0x1f))); + + writer.write8((uint8_t) ((c.seq_tier_0 ? 0x80 : 0) | + (c.high_bitdepth ? 0x40 : 0) | + (c.twelve_bit ? 0x20 : 0) | + (c.monochrome ? 0x10 : 0) | + (c.chroma_subsampling_x ? 0x08 : 0) | + (c.chroma_subsampling_y ? 0x04 : 0) | + (c.chroma_sample_position & 0x03))); + + writer.write8(0); // TODO initial_presentation_delay + + prepend_header(writer, box_start); + + return Error::Ok; +} + + +std::string Box_av1C::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + + const auto& c = m_configuration; // abbreviation + + sstr << indent << "version: " << ((int) c.version) << "\n" + << indent << "seq_profile: " << ((int) c.seq_profile) << "\n" + << indent << "seq_level_idx_0: " << ((int) c.seq_level_idx_0) << "\n" + << indent << "high_bitdepth: " << ((int) c.high_bitdepth) << "\n" + << indent << "twelve_bit: " << ((int) c.twelve_bit) << "\n" + << indent << "chroma_subsampling_x: " << ((int) c.chroma_subsampling_x) << "\n" + << indent << "chroma_subsampling_y: " << ((int) c.chroma_subsampling_y) << "\n" + << indent << "chroma_sample_position: " << ((int) c.chroma_sample_position) << "\n" + << indent << "initial_presentation_delay: "; + + if (c.initial_presentation_delay_present) { + sstr << c.initial_presentation_delay_minus_one + 1 << "\n"; + } + else { + sstr << "not present\n"; + } + + sstr << indent << "config OBUs:"; + for (size_t i = 0; i < m_config_OBUs.size(); i++) { + sstr << " " << std::hex << std::setfill('0') << std::setw(2) + << ((int) m_config_OBUs[i]); + } + sstr << std::dec << "\n"; + + return sstr.str(); +} + + Error fill_av1C_configuration(Box_av1C::configuration* inout_config, const std::shared_ptr& image) { int bpp = image->get_bits_per_pixel(heif_channel_Y); @@ -69,6 +178,87 @@ Error fill_av1C_configuration(Box_av1C::configuration* inout_config, const std:: } +Error Box_a1op::parse(BitstreamRange& range) +{ + op_index = range.read8(); + + return range.get_error(); +} + + +std::string Box_a1op::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + + sstr << indent << "op-index: " << ((int) op_index) << "\n"; + + return sstr.str(); +} + + +Error Box_a1op::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + writer.write8(op_index); + + prepend_header(writer, box_start); + + return Error::Ok; +} + + +Error Box_a1lx::parse(BitstreamRange& range) +{ + uint8_t flags = range.read8(); + + for (int i = 0; i < 3; i++) { + if (flags & 1) { + layer_size[i] = range.read32(); + } + else { + layer_size[i] = range.read16(); + } + } + + return range.get_error(); +} + + +std::string Box_a1lx::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + + sstr << indent << "layer-sizes: [" << layer_size[0] << "," << layer_size[1] << "," << layer_size[2] << "]\n"; + + return sstr.str(); +} + + +Error Box_a1lx::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + bool large = (layer_size[0] > 0xFFFF || layer_size[1] > 0xFFFF || layer_size[2] > 0xFFFF); + writer.write8(large ? 1 : 0); + + for (int i = 0; i < 3; i++) { + if (large) { + writer.write32(layer_size[i]); + } + else { + writer.write16((uint16_t) layer_size[i]); + } + } + + prepend_header(writer, box_start); + + return Error::Ok; +} + + static uint64_t leb128(BitReader& reader) { uint64_t val = 0; diff --git a/libheif/avif.h b/libheif/avif.h index 887ba2ef10..e1a3fab3c1 100644 --- a/libheif/avif.h +++ b/libheif/avif.h @@ -24,12 +24,125 @@ #include #include #include +#include +#include #include "heif.h" #include "box.h" #include "error.h" + +class Box_av1C : public Box +{ +public: + Box_av1C() + { + set_short_type(fourcc("av1C")); + } + + struct configuration + { + //unsigned int (1) marker = 1; + uint8_t version = 1; + uint8_t seq_profile = 0; + uint8_t seq_level_idx_0 = 0; + uint8_t seq_tier_0 = 0; + uint8_t high_bitdepth = 0; + uint8_t twelve_bit = 0; + uint8_t monochrome = 0; + uint8_t chroma_subsampling_x = 0; + uint8_t chroma_subsampling_y = 0; + uint8_t chroma_sample_position = 0; + //uint8_t reserved = 0; + + uint8_t initial_presentation_delay_present = 0; + uint8_t initial_presentation_delay_minus_one = 0; + + //unsigned int (8)[] configOBUs; + + heif_chroma get_heif_chroma() const { + if (chroma_subsampling_x==2 && chroma_subsampling_y==2) { + return heif_chroma_420; + } + else if (chroma_subsampling_x==2 && chroma_subsampling_y==1) { + return heif_chroma_422; + } + else if (chroma_subsampling_x==1 && chroma_subsampling_y==1) { + return heif_chroma_444; + } + else { + return heif_chroma_undefined; + } + } + }; + + + std::string dump(Indent&) const override; + + bool get_headers(std::vector* dest) const + { + *dest = m_config_OBUs; + return true; + } + + void set_configuration(const configuration& config) { m_configuration = config; } + + const configuration& get_configuration() const { return m_configuration; } + + //void append_nal_data(const std::vector& nal); + //void append_nal_data(const uint8_t* data, size_t size); + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; + +private: + configuration m_configuration; + + std::vector m_config_OBUs; +}; + + +class Box_a1op : public Box +{ +public: + Box_a1op() + { + set_short_type(fourcc("a1op")); + } + + uint8_t op_index = 0; + + std::string dump(Indent&) const override; + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; +}; + + +class Box_a1lx : public Box +{ +public: + Box_a1lx() + { + set_short_type(fourcc("a1lx")); + } + + uint32_t layer_size[3]{}; + + std::string dump(Indent&) const override; + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; +}; + + class HeifPixelImage; Error fill_av1C_configuration(Box_av1C::configuration* inout_config, const std::shared_ptr& image); diff --git a/libheif/box.cc b/libheif/box.cc index b7679611ac..ca4c2f4758 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -2135,87 +2135,6 @@ Error Box_lsel::write(StreamWriter& writer) const } -Error Box_a1op::parse(BitstreamRange& range) -{ - op_index = range.read8(); - - return range.get_error(); -} - - -std::string Box_a1op::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << Box::dump(indent); - - sstr << indent << "op-index: " << ((int) op_index) << "\n"; - - return sstr.str(); -} - - -Error Box_a1op::write(StreamWriter& writer) const -{ - size_t box_start = reserve_box_header_space(writer); - - writer.write8(op_index); - - prepend_header(writer, box_start); - - return Error::Ok; -} - - -Error Box_a1lx::parse(BitstreamRange& range) -{ - uint8_t flags = range.read8(); - - for (int i = 0; i < 3; i++) { - if (flags & 1) { - layer_size[i] = range.read32(); - } - else { - layer_size[i] = range.read16(); - } - } - - return range.get_error(); -} - - -std::string Box_a1lx::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << Box::dump(indent); - - sstr << indent << "layer-sizes: [" << layer_size[0] << "," << layer_size[1] << "," << layer_size[2] << "]\n"; - - return sstr.str(); -} - - -Error Box_a1lx::write(StreamWriter& writer) const -{ - size_t box_start = reserve_box_header_space(writer); - - bool large = (layer_size[0] > 0xFFFF || layer_size[1] > 0xFFFF || layer_size[2] > 0xFFFF); - writer.write8(large ? 1 : 0); - - for (int i = 0; i < 3; i++) { - if (large) { - writer.write32(layer_size[i]); - } - else { - writer.write16((uint16_t) layer_size[i]); - } - } - - prepend_header(writer, box_start); - - return Error::Ok; -} - - Error Box_clli::parse(BitstreamRange& range) { //parse_full_box_header(range); @@ -3044,113 +2963,6 @@ void Box_iref::add_reference(heif_item_id from_id, uint32_t type, const std::vec } -Error Box_av1C::parse(BitstreamRange& range) -{ - //parse_full_box_header(range); - - uint8_t byte; - - auto& c = m_configuration; // abbreviation - - byte = range.read8(); - if ((byte & 0x80) == 0) { - // error: marker bit not set - } - - c.version = byte & 0x7F; - - byte = range.read8(); - c.seq_profile = (byte >> 5) & 0x7; - c.seq_level_idx_0 = byte & 0x1f; - - byte = range.read8(); - c.seq_tier_0 = (byte >> 7) & 1; - c.high_bitdepth = (byte >> 6) & 1; - c.twelve_bit = (byte >> 5) & 1; - c.monochrome = (byte >> 4) & 1; - c.chroma_subsampling_x = (byte >> 3) & 1; - c.chroma_subsampling_y = (byte >> 2) & 1; - c.chroma_sample_position = byte & 3; - - byte = range.read8(); - c.initial_presentation_delay_present = (byte >> 4) & 1; - if (c.initial_presentation_delay_present) { - c.initial_presentation_delay_minus_one = byte & 0x0F; - } - - const size_t configOBUs_bytes = range.get_remaining_bytes(); - m_config_OBUs.resize(configOBUs_bytes); - - if (!range.read(m_config_OBUs.data(), configOBUs_bytes)) { - // error - } - - return range.get_error(); -} - - -Error Box_av1C::write(StreamWriter& writer) const -{ - size_t box_start = reserve_box_header_space(writer); - - const auto& c = m_configuration; // abbreviation - - writer.write8(c.version | 0x80); - - writer.write8((uint8_t) (((c.seq_profile & 0x7) << 5) | - (c.seq_level_idx_0 & 0x1f))); - - writer.write8((uint8_t) ((c.seq_tier_0 ? 0x80 : 0) | - (c.high_bitdepth ? 0x40 : 0) | - (c.twelve_bit ? 0x20 : 0) | - (c.monochrome ? 0x10 : 0) | - (c.chroma_subsampling_x ? 0x08 : 0) | - (c.chroma_subsampling_y ? 0x04 : 0) | - (c.chroma_sample_position & 0x03))); - - writer.write8(0); // TODO initial_presentation_delay - - prepend_header(writer, box_start); - - return Error::Ok; -} - - -std::string Box_av1C::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << Box::dump(indent); - - const auto& c = m_configuration; // abbreviation - - sstr << indent << "version: " << ((int) c.version) << "\n" - << indent << "seq_profile: " << ((int) c.seq_profile) << "\n" - << indent << "seq_level_idx_0: " << ((int) c.seq_level_idx_0) << "\n" - << indent << "high_bitdepth: " << ((int) c.high_bitdepth) << "\n" - << indent << "twelve_bit: " << ((int) c.twelve_bit) << "\n" - << indent << "chroma_subsampling_x: " << ((int) c.chroma_subsampling_x) << "\n" - << indent << "chroma_subsampling_y: " << ((int) c.chroma_subsampling_y) << "\n" - << indent << "chroma_sample_position: " << ((int) c.chroma_sample_position) << "\n" - << indent << "initial_presentation_delay: "; - - if (c.initial_presentation_delay_present) { - sstr << c.initial_presentation_delay_minus_one + 1 << "\n"; - } - else { - sstr << "not present\n"; - } - - sstr << indent << "config OBUs:"; - for (size_t i = 0; i < m_config_OBUs.size(); i++) { - sstr << " " << std::hex << std::setfill('0') << std::setw(2) - << ((int) m_config_OBUs[i]); - } - sstr << std::dec << "\n"; - - return sstr.str(); -} - - Error Box_vvcC::parse(BitstreamRange& range) { //parse_full_box_header(range); diff --git a/libheif/box.h b/libheif/box.h index 6de6b4ad6d..a58cbb8a63 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -770,78 +770,6 @@ class Box_iref : public FullBox }; -class Box_av1C : public Box -{ -public: - Box_av1C() - { - set_short_type(fourcc("av1C")); - } - - struct configuration - { - //unsigned int (1) marker = 1; - uint8_t version = 1; - uint8_t seq_profile = 0; - uint8_t seq_level_idx_0 = 0; - uint8_t seq_tier_0 = 0; - uint8_t high_bitdepth = 0; - uint8_t twelve_bit = 0; - uint8_t monochrome = 0; - uint8_t chroma_subsampling_x = 0; - uint8_t chroma_subsampling_y = 0; - uint8_t chroma_sample_position = 0; - //uint8_t reserved = 0; - - uint8_t initial_presentation_delay_present = 0; - uint8_t initial_presentation_delay_minus_one = 0; - - //unsigned int (8)[] configOBUs; - - heif_chroma get_heif_chroma() const { - if (chroma_subsampling_x==2 && chroma_subsampling_y==2) { - return heif_chroma_420; - } - else if (chroma_subsampling_x==2 && chroma_subsampling_y==1) { - return heif_chroma_422; - } - else if (chroma_subsampling_x==1 && chroma_subsampling_y==1) { - return heif_chroma_444; - } - else { - return heif_chroma_undefined; - } - } - }; - - - std::string dump(Indent&) const override; - - bool get_headers(std::vector* dest) const - { - *dest = m_config_OBUs; - return true; - } - - void set_configuration(const configuration& config) { m_configuration = config; } - - const configuration& get_configuration() const { return m_configuration; } - - //void append_nal_data(const std::vector& nal); - //void append_nal_data(const uint8_t* data, size_t size); - - Error write(StreamWriter& writer) const override; - -protected: - Error parse(BitstreamRange& range) override; - -private: - configuration m_configuration; - - std::vector m_config_OBUs; -}; - - class Box_vvcC : public Box { public: @@ -1061,44 +989,6 @@ class Box_lsel : public Box }; -class Box_a1op : public Box -{ -public: - Box_a1op() - { - set_short_type(fourcc("a1op")); - } - - uint8_t op_index = 0; - - std::string dump(Indent&) const override; - - Error write(StreamWriter& writer) const override; - -protected: - Error parse(BitstreamRange& range) override; -}; - - -class Box_a1lx : public Box -{ -public: - Box_a1lx() - { - set_short_type(fourcc("a1lx")); - } - - uint32_t layer_size[3]{}; - - std::string dump(Indent&) const override; - - Error write(StreamWriter& writer) const override; - -protected: - Error parse(BitstreamRange& range) override; -}; - - class Box_clli : public Box { public: diff --git a/libheif/file.h b/libheif/file.h index 9b6e839b8b..d812bd09e5 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -21,6 +21,7 @@ #ifndef LIBHEIF_FILE_H #define LIBHEIF_FILE_H +#include "avif.h" #include "box.h" #include "hevc.h" From 5a5fdc50bd31a1b23f02b0f6a5ceb623ca84d5d0 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Tue, 11 Jul 2023 19:52:39 +1000 Subject: [PATCH 3/5] split off jpgC into JPEG specific files --- libheif/CMakeLists.txt | 2 ++ libheif/box.cc | 33 +------------------------- libheif/box.h | 24 ------------------- libheif/context.cc | 1 + libheif/file.cc | 1 + libheif/jpeg.cc | 53 ++++++++++++++++++++++++++++++++++++++++++ libheif/jpeg.h | 52 +++++++++++++++++++++++++++++++++++++++++ 7 files changed, 110 insertions(+), 56 deletions(-) create mode 100644 libheif/jpeg.cc create mode 100644 libheif/jpeg.h diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 3b8c2fc7d5..994d83ed48 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -61,6 +61,8 @@ set(libheif_sources color-conversion/alpha.h color-conversion/chroma_sampling.cc color-conversion/chroma_sampling.h + jpeg.h + jpeg.cc jpeg2000.h jpeg2000.cc ${libheif_headers}) diff --git a/libheif/box.cc b/libheif/box.cc index ca4c2f4758..a00ffdfa7f 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -23,6 +23,7 @@ #include "box.h" #include "security_limits.h" #include "nclx.h" +#include "jpeg.h" #include "jpeg2000.h" #include "hevc.h" #include "mask_image.h" @@ -3298,38 +3299,6 @@ std::string Box_url::dump(Indent& indent) const } -std::string Box_jpgC::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << Box::dump(indent); - - sstr << indent << "num bytes: " << m_data.size() << "\n"; - - return sstr.str(); -} - - -Error Box_jpgC::write(StreamWriter& writer) const -{ - size_t box_start = reserve_box_header_space(writer); - - writer.write(m_data); - - prepend_header(writer, box_start); - - return Error::Ok; -} - - -Error Box_jpgC::parse(BitstreamRange& range) -{ - size_t nBytes = range.get_remaining_bytes(); - m_data.resize(nBytes); - range.read(m_data.data(), nBytes); - return range.get_error(); -} - - Error Box_udes::parse(BitstreamRange& range) { parse_full_box_header(range); diff --git a/libheif/box.h b/libheif/box.h index a58cbb8a63..bd227d7301 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -1135,30 +1135,6 @@ class Box_colr : public Box }; -class Box_jpgC : public Box -{ -public: - Box_jpgC() - { - set_short_type(fourcc("jpgC")); - } - - const std::vector& get_data() { return m_data; } - - void set_data(const std::vector& data) { m_data = data; } - - std::string dump(Indent&) const override; - - Error write(StreamWriter& writer) const override; - -protected: - Error parse(BitstreamRange& range) override; - -private: - std::vector m_data; -}; - - /** * User Description property. * diff --git a/libheif/context.cc b/libheif/context.cc index 359a5b74f3..94276da90f 100644 --- a/libheif/context.cc +++ b/libheif/context.cc @@ -42,6 +42,7 @@ #include "security_limits.h" #include "hevc.h" #include "avif.h" +#include "jpeg.h" #include "plugin_registry.h" #include "libheif/color-conversion/colorconversion.h" #include "mask_image.h" diff --git a/libheif/file.cc b/libheif/file.cc index 1a2904265b..1874bf390a 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -22,6 +22,7 @@ #include "libheif/box.h" #include "libheif/heif.h" #include "libheif/jpeg2000.h" +#include "libheif/jpeg.h" #include #include diff --git a/libheif/jpeg.cc b/libheif/jpeg.cc new file mode 100644 index 0000000000..bb71587aed --- /dev/null +++ b/libheif/jpeg.cc @@ -0,0 +1,53 @@ +/* + * HEIF JPEG codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#include "jpeg.h" +#include + +std::string Box_jpgC::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + + sstr << indent << "num bytes: " << m_data.size() << "\n"; + + return sstr.str(); +} + + +Error Box_jpgC::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + writer.write(m_data); + + prepend_header(writer, box_start); + + return Error::Ok; +} + + +Error Box_jpgC::parse(BitstreamRange& range) +{ + size_t nBytes = range.get_remaining_bytes(); + m_data.resize(nBytes); + range.read(m_data.data(), nBytes); + return range.get_error(); +} diff --git a/libheif/jpeg.h b/libheif/jpeg.h new file mode 100644 index 0000000000..3958bfaccf --- /dev/null +++ b/libheif/jpeg.h @@ -0,0 +1,52 @@ +/* + * HEIF JPEG codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef LIBHEIF_JPEG_H +#define LIBHEIF_JPEG_H + +#include "box.h" +#include +#include + +class Box_jpgC : public Box +{ +public: + Box_jpgC() + { + set_short_type(fourcc("jpgC")); + } + + const std::vector& get_data() { return m_data; } + + void set_data(const std::vector& data) { m_data = data; } + + std::string dump(Indent&) const override; + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; + +private: + std::vector m_data; +}; + + +#endif // LIBHEIF_JPEG_H From ad52bb5c314b0e87ae3fbda660fb9187da5ef716 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Tue, 11 Jul 2023 20:13:55 +1000 Subject: [PATCH 4/5] migrate VVC box to separate file --- libheif/CMakeLists.txt | 2 + libheif/box.cc | 107 +--------------------------------- libheif/box.h | 69 ---------------------- libheif/file.cc | 1 + libheif/vvc.cc | 128 +++++++++++++++++++++++++++++++++++++++++ libheif/vvc.h | 98 +++++++++++++++++++++++++++++++ 6 files changed, 230 insertions(+), 175 deletions(-) create mode 100644 libheif/vvc.cc create mode 100644 libheif/vvc.h diff --git a/libheif/CMakeLists.txt b/libheif/CMakeLists.txt index 994d83ed48..72828abdc4 100644 --- a/libheif/CMakeLists.txt +++ b/libheif/CMakeLists.txt @@ -65,6 +65,8 @@ set(libheif_sources jpeg.cc jpeg2000.h jpeg2000.cc + vvc.h + vvc.cc ${libheif_headers}) add_library(heif ${libheif_sources}) diff --git a/libheif/box.cc b/libheif/box.cc index a00ffdfa7f..7915dae225 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -27,6 +27,7 @@ #include "jpeg2000.h" #include "hevc.h" #include "mask_image.h" +#include "vvc.h" #include #include @@ -2964,112 +2965,6 @@ void Box_iref::add_reference(heif_item_id from_id, uint32_t type, const std::vec } -Error Box_vvcC::parse(BitstreamRange& range) -{ - //parse_full_box_header(range); - - uint8_t byte; - - auto& c = m_configuration; // abbreviation - - c.configurationVersion = range.read8(); - c.avgFrameRate_times_256 = range.read16(); - - //printf("version: %d\n", c.configurationVersion); - - byte = range.read8(); - c.constantFrameRate = (byte & 0xc0) >> 6; - c.numTemporalLayers = (byte & 0x38) >> 3; - c.lengthSize = (byte & 0x06) + 1; - c.ptl_present_flag = (byte & 0x01); - // assert(c.ptl_present_flag == false); // TODO (removed the assert since it will trigger the fuzzers) - - byte = range.read8(); - c.chroma_format_present_flag = (byte & 0x80); - c.chroma_format_idc = (byte & 0x60) >> 5; - - c.bit_depth_present_flag = (byte & 0x10); - c.bit_depth = ((byte & 0x0e) >> 1) + 8; - - c.numOfArrays = range.read8(); - -#if 0 - const int64_t configOBUs_bytes = range.get_remaining_bytes(); - m_config_OBUs.resize(configOBUs_bytes); - - if (!range.read(m_config_OBUs.data(), configOBUs_bytes)) { - // error - } -#endif - - return range.get_error(); -} - - -Error Box_vvcC::write(StreamWriter& writer) const -{ - size_t box_start = reserve_box_header_space(writer); - -#if 0 - const auto& c = m_configuration; // abbreviation - - writer.write8(c.version | 0x80); - - writer.write8((uint8_t) (((c.seq_profile & 0x7) << 5) | - (c.seq_level_idx_0 & 0x1f))); -#endif - - prepend_header(writer, box_start); - - return Error::Ok; -} - - -static const char* vvc_chroma_names[4] = {"mono", "4:2:0", "4:2:2", "4:4:4"}; - -std::string Box_vvcC::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << Box::dump(indent); - - const auto& c = m_configuration; // abbreviation - - sstr << indent << "version: " << ((int) c.configurationVersion) << "\n" - << indent << "frame-rate: " << (c.avgFrameRate_times_256 / 256.0f) << "\n" - << indent << "constant frame rate: " << (c.constantFrameRate == 1 ? "constant" : (c.constantFrameRate == 2 ? "multi-layer" : "unknown")) << "\n" - << indent << "num temporal layers: " << ((int) c.numTemporalLayers) << "\n" - << indent << "length size: " << ((int) c.lengthSize) << "\n" - << indent << "chroma-format: "; - if (c.chroma_format_present_flag) { - sstr << vvc_chroma_names[c.chroma_format_idc] << "\n"; - } - else { - sstr << "---\n"; - } - - sstr << indent << "bit-depth: "; - if (c.bit_depth_present_flag) { - sstr << ((int) c.bit_depth) << "\n"; - } - else { - sstr << "---\n"; - } - - sstr << "num of arrays: " << ((int) c.numOfArrays) << "\n"; - -#if 0 - sstr << indent << "config OBUs:"; - for (size_t i = 0; i < m_config_OBUs.size(); i++) { - sstr << " " << std::hex << std::setfill('0') << std::setw(2) - << ((int) m_config_OBUs[i]); - } - sstr << std::dec << "\n"; -#endif - - return sstr.str(); -} - - Error Box_idat::parse(BitstreamRange& range) { //parse_full_box_header(range); diff --git a/libheif/box.h b/libheif/box.h index bd227d7301..67e6eab502 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -770,75 +770,6 @@ class Box_iref : public FullBox }; -class Box_vvcC : public Box -{ -public: - Box_vvcC() - { - set_short_type(fourcc("vvcC")); - } - - struct configuration - { - uint8_t configurationVersion = 1; - uint16_t avgFrameRate_times_256; - uint8_t constantFrameRate; - uint8_t numTemporalLayers; - uint8_t lengthSize; - bool ptl_present_flag; - //if (ptl_present_flag) { - // VvcPTLRecord(numTemporalLayers) track_ptl; - // uint16_t output_layer_set_idx; - //} - bool chroma_format_present_flag; - uint8_t chroma_format_idc; - - bool bit_depth_present_flag; - uint8_t bit_depth; - - uint8_t numOfArrays; -#if 0 - for (j=0; j < numOfArrays; j++) { - unsigned int(1) array_completeness; - bit(1) reserved = 0; - unsigned int(6) NAL_unit_type; - unsigned int(16) numNalus; - for (i=0; i< numNalus; i++) { - unsigned int(16) nalUnitLength; - bit(8*nalUnitLength) nalUnit; - } - } -#endif - }; - - - std::string dump(Indent&) const override; - - bool get_headers(std::vector* dest) const - { - *dest = m_config_NALs; - return true; - } - - void set_configuration(const configuration& config) { m_configuration = config; } - - const configuration& get_configuration() const { return m_configuration; } - - //void append_nal_data(const std::vector& nal); - //void append_nal_data(const uint8_t* data, size_t size); - - Error write(StreamWriter& writer) const override; - -protected: - Error parse(BitstreamRange& range) override; - -private: - configuration m_configuration; - - std::vector m_config_NALs; -}; - - class Box_idat : public Box { public: diff --git a/libheif/file.cc b/libheif/file.cc index 1874bf390a..178a08e6a0 100644 --- a/libheif/file.cc +++ b/libheif/file.cc @@ -23,6 +23,7 @@ #include "libheif/heif.h" #include "libheif/jpeg2000.h" #include "libheif/jpeg.h" +#include "libheif/vvc.h" #include #include diff --git a/libheif/vvc.cc b/libheif/vvc.cc new file mode 100644 index 0000000000..ecea732287 --- /dev/null +++ b/libheif/vvc.cc @@ -0,0 +1,128 @@ +/* + * HEIF VVC codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#include "vvc.h" +#include + +Error Box_vvcC::parse(BitstreamRange& range) +{ + //parse_full_box_header(range); + + uint8_t byte; + + auto& c = m_configuration; // abbreviation + + c.configurationVersion = range.read8(); + c.avgFrameRate_times_256 = range.read16(); + + //printf("version: %d\n", c.configurationVersion); + + byte = range.read8(); + c.constantFrameRate = (byte & 0xc0) >> 6; + c.numTemporalLayers = (byte & 0x38) >> 3; + c.lengthSize = (byte & 0x06) + 1; + c.ptl_present_flag = (byte & 0x01); + // assert(c.ptl_present_flag == false); // TODO (removed the assert since it will trigger the fuzzers) + + byte = range.read8(); + c.chroma_format_present_flag = (byte & 0x80); + c.chroma_format_idc = (byte & 0x60) >> 5; + + c.bit_depth_present_flag = (byte & 0x10); + c.bit_depth = ((byte & 0x0e) >> 1) + 8; + + c.numOfArrays = range.read8(); + +#if 0 + const int64_t configOBUs_bytes = range.get_remaining_bytes(); + m_config_OBUs.resize(configOBUs_bytes); + + if (!range.read(m_config_OBUs.data(), configOBUs_bytes)) { + // error + } +#endif + + return range.get_error(); +} + + +Error Box_vvcC::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + +#if 0 + const auto& c = m_configuration; // abbreviation + + writer.write8(c.version | 0x80); + + writer.write8((uint8_t) (((c.seq_profile & 0x7) << 5) | + (c.seq_level_idx_0 & 0x1f))); +#endif + + prepend_header(writer, box_start); + + return Error::Ok; +} + + +static const char* vvc_chroma_names[4] = {"mono", "4:2:0", "4:2:2", "4:4:4"}; + +std::string Box_vvcC::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + + const auto& c = m_configuration; // abbreviation + + sstr << indent << "version: " << ((int) c.configurationVersion) << "\n" + << indent << "frame-rate: " << (c.avgFrameRate_times_256 / 256.0f) << "\n" + << indent << "constant frame rate: " << (c.constantFrameRate == 1 ? "constant" : (c.constantFrameRate == 2 ? "multi-layer" : "unknown")) << "\n" + << indent << "num temporal layers: " << ((int) c.numTemporalLayers) << "\n" + << indent << "length size: " << ((int) c.lengthSize) << "\n" + << indent << "chroma-format: "; + if (c.chroma_format_present_flag) { + sstr << vvc_chroma_names[c.chroma_format_idc] << "\n"; + } + else { + sstr << "---\n"; + } + + sstr << indent << "bit-depth: "; + if (c.bit_depth_present_flag) { + sstr << ((int) c.bit_depth) << "\n"; + } + else { + sstr << "---\n"; + } + + sstr << "num of arrays: " << ((int) c.numOfArrays) << "\n"; + +#if 0 + sstr << indent << "config OBUs:"; + for (size_t i = 0; i < m_config_OBUs.size(); i++) { + sstr << " " << std::hex << std::setfill('0') << std::setw(2) + << ((int) m_config_OBUs[i]); + } + sstr << std::dec << "\n"; +#endif + + return sstr.str(); +} + diff --git a/libheif/vvc.h b/libheif/vvc.h new file mode 100644 index 0000000000..ce51331813 --- /dev/null +++ b/libheif/vvc.h @@ -0,0 +1,98 @@ +/* + * HEIF VVC codec. + * Copyright (c) 2023 Dirk Farin + * + * This file is part of libheif. + * + * libheif is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * libheif is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with libheif. If not, see . + */ + +#ifndef LIBHEIF_VVC_H +#define LIBHEIF_VVC_H + +#include "box.h" +#include +#include + + +class Box_vvcC : public Box +{ +public: + Box_vvcC() + { + set_short_type(fourcc("vvcC")); + } + + struct configuration + { + uint8_t configurationVersion = 1; + uint16_t avgFrameRate_times_256; + uint8_t constantFrameRate; + uint8_t numTemporalLayers; + uint8_t lengthSize; + bool ptl_present_flag; + //if (ptl_present_flag) { + // VvcPTLRecord(numTemporalLayers) track_ptl; + // uint16_t output_layer_set_idx; + //} + bool chroma_format_present_flag; + uint8_t chroma_format_idc; + + bool bit_depth_present_flag; + uint8_t bit_depth; + + uint8_t numOfArrays; +#if 0 + for (j=0; j < numOfArrays; j++) { + unsigned int(1) array_completeness; + bit(1) reserved = 0; + unsigned int(6) NAL_unit_type; + unsigned int(16) numNalus; + for (i=0; i< numNalus; i++) { + unsigned int(16) nalUnitLength; + bit(8*nalUnitLength) nalUnit; + } + } +#endif + }; + + + std::string dump(Indent&) const override; + + bool get_headers(std::vector* dest) const + { + *dest = m_config_NALs; + return true; + } + + void set_configuration(const configuration& config) { m_configuration = config; } + + const configuration& get_configuration() const { return m_configuration; } + + //void append_nal_data(const std::vector& nal); + //void append_nal_data(const uint8_t* data, size_t size); + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; + +private: + configuration m_configuration; + + std::vector m_config_NALs; +}; + + +#endif // LIBHEIF_VVC_H From 448936589e2e84e7cd971585bb6cb4372632fdf6 Mon Sep 17 00:00:00 2001 From: Brad Hards Date: Tue, 11 Jul 2023 20:41:46 +1000 Subject: [PATCH 5/5] migrate colour boxes into nclx specific file --- libheif/box.cc | 240 ----------------------------------------- libheif/box.h | 108 ------------------- libheif/file.h | 1 + libheif/nclx.cc | 248 +++++++++++++++++++++++++++++++++++++++++++ libheif/nclx.h | 113 ++++++++++++++++++++ libheif/pixelimage.h | 2 +- 6 files changed, 363 insertions(+), 349 deletions(-) diff --git a/libheif/box.cc b/libheif/box.cc index 7915dae225..9c9692725e 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -1768,246 +1768,6 @@ std::string Box_ipco::dump(Indent& indent) const return sstr.str(); } -Error color_profile_nclx::parse(BitstreamRange& range) -{ - StreamReader::grow_status status; - status = range.wait_for_available_bytes(7); - if (status != StreamReader::size_reached) { - // TODO: return recoverable error at timeout - return Error(heif_error_Invalid_input, - heif_suberror_End_of_data); - } - - m_colour_primaries = range.read16(); - m_transfer_characteristics = range.read16(); - m_matrix_coefficients = range.read16(); - m_full_range_flag = (range.read8() & 0x80 ? true : false); - - return Error::Ok; -} - -Error color_profile_nclx::get_nclx_color_profile(struct heif_color_profile_nclx** out_data) const -{ - *out_data = nullptr; - - struct heif_color_profile_nclx* nclx = alloc_nclx_color_profile(); - - if (nclx == nullptr) { - return Error(heif_error_Memory_allocation_error, - heif_suberror_Unspecified); - } - - struct heif_error err; - - nclx->version = 1; - - err = heif_nclx_color_profile_set_color_primaries(nclx, get_colour_primaries()); - if (err.code) { - free_nclx_color_profile(nclx); - return {err.code, err.subcode}; - } - - err = heif_nclx_color_profile_set_transfer_characteristics(nclx, get_transfer_characteristics()); - if (err.code) { - free_nclx_color_profile(nclx); - return {err.code, err.subcode}; - } - - err = heif_nclx_color_profile_set_matrix_coefficients(nclx, get_matrix_coefficients()); - if (err.code) { - free_nclx_color_profile(nclx); - return {err.code, err.subcode}; - } - - nclx->full_range_flag = get_full_range_flag(); - - // fill color primaries - - auto primaries = ::get_colour_primaries(nclx->color_primaries); - - nclx->color_primary_red_x = primaries.redX; - nclx->color_primary_red_y = primaries.redY; - nclx->color_primary_green_x = primaries.greenX; - nclx->color_primary_green_y = primaries.greenY; - nclx->color_primary_blue_x = primaries.blueX; - nclx->color_primary_blue_y = primaries.blueY; - nclx->color_primary_white_x = primaries.whiteX; - nclx->color_primary_white_y = primaries.whiteY; - - *out_data = nclx; - - return Error::Ok; -} - - -struct heif_color_profile_nclx* color_profile_nclx::alloc_nclx_color_profile() -{ - auto profile = (heif_color_profile_nclx*) malloc(sizeof(struct heif_color_profile_nclx)); - - if (profile) { - profile->version = 1; - profile->color_primaries = heif_color_primaries_unspecified; - profile->transfer_characteristics = heif_transfer_characteristic_unspecified; - profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6; - profile->full_range_flag = true; - } - - return profile; -} - - -void color_profile_nclx::free_nclx_color_profile(struct heif_color_profile_nclx* profile) -{ - free(profile); -} - - -void color_profile_nclx::set_default() -{ - m_colour_primaries = 2; - m_transfer_characteristics = 2; - m_matrix_coefficients = 6; - m_full_range_flag = true; -} - - -void color_profile_nclx::set_undefined() -{ - m_colour_primaries = 2; - m_transfer_characteristics = 2; - m_matrix_coefficients = 2; - m_full_range_flag = true; -} - - -void color_profile_nclx::set_from_heif_color_profile_nclx(const struct heif_color_profile_nclx* nclx) -{ - if (nclx) { - m_colour_primaries = nclx->color_primaries; - m_transfer_characteristics = nclx->transfer_characteristics; - m_matrix_coefficients = nclx->matrix_coefficients; - m_full_range_flag = nclx->full_range_flag; - } -} - - -Error Box_colr::parse(BitstreamRange& range) -{ - StreamReader::grow_status status; - uint32_t colour_type = range.read32(); - - if (colour_type == fourcc("nclx")) { - auto color_profile = std::make_shared(); - m_color_profile = color_profile; - Error err = color_profile->parse(range); - if (err) { - return err; - } - } - else if (colour_type == fourcc("prof") || - colour_type == fourcc("rICC")) { - uint64_t profile_size_64 = get_box_size() - get_header_size() - 4; - if (profile_size_64 > std::numeric_limits::max()) { - return Error(heif_error_Invalid_input, heif_suberror_Security_limit_exceeded, "Color profile exceeds maximum supported size"); - } - - size_t profile_size = static_cast(profile_size_64); - - status = range.wait_for_available_bytes(profile_size); - if (status != StreamReader::size_reached) { - // TODO: return recoverable error at timeout - return Error(heif_error_Invalid_input, - heif_suberror_End_of_data); - } - - std::vector rawData(profile_size); - for (size_t i = 0; i < profile_size; i++) { - rawData[i] = range.read8(); - } - - m_color_profile = std::make_shared(colour_type, rawData); - } - else { - return Error(heif_error_Invalid_input, - heif_suberror_Unknown_color_profile_type); - } - - return range.get_error(); -} - - -std::string Box_colr::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << Box::dump(indent); - - if (m_color_profile) { - sstr << indent << "colour_type: " << to_fourcc(get_color_profile_type()) << "\n"; - sstr << m_color_profile->dump(indent); - } - else { - sstr << indent << "colour_type: ---\n"; - sstr << "no color profile\n"; - } - - return sstr.str(); -} - - -std::string color_profile_raw::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << indent << "profile size: " << m_data.size() << "\n"; - return sstr.str(); -} - - -std::string color_profile_nclx::dump(Indent& indent) const -{ - std::ostringstream sstr; - sstr << indent << "colour_primaries: " << m_colour_primaries << "\n" - << indent << "transfer_characteristics: " << m_transfer_characteristics << "\n" - << indent << "matrix_coefficients: " << m_matrix_coefficients << "\n" - << indent << "full_range_flag: " << m_full_range_flag << "\n"; - return sstr.str(); -} - - -Error color_profile_nclx::write(StreamWriter& writer) const -{ - writer.write16(m_colour_primaries); - writer.write16(m_transfer_characteristics); - writer.write16(m_matrix_coefficients); - writer.write8(m_full_range_flag ? 0x80 : 0x00); - - return Error::Ok; -} - -Error color_profile_raw::write(StreamWriter& writer) const -{ - writer.write(m_data); - - return Error::Ok; -} - -Error Box_colr::write(StreamWriter& writer) const -{ - size_t box_start = reserve_box_header_space(writer); - - assert(m_color_profile); - - writer.write32(m_color_profile->get_type()); - - Error err = m_color_profile->write(writer); - if (err) { - return err; - } - - prepend_header(writer, box_start); - - return Error::Ok; -} - Error Box_pixi::parse(BitstreamRange& range) { diff --git a/libheif/box.h b/libheif/box.h index 67e6eab502..4a4d5d24f1 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -958,114 +958,6 @@ class Box_mdcv : public Box }; -class color_profile -{ -public: - virtual ~color_profile() = default; - - virtual uint32_t get_type() const = 0; - - virtual std::string dump(Indent&) const = 0; - - virtual Error write(StreamWriter& writer) const = 0; -}; - -class color_profile_raw : public color_profile -{ -public: - color_profile_raw(uint32_t type, const std::vector& data) - : m_type(type), m_data(data) {} - - uint32_t get_type() const override { return m_type; } - - const std::vector& get_data() const { return m_data; } - - std::string dump(Indent&) const override; - - Error write(StreamWriter& writer) const override; - -private: - uint32_t m_type; - std::vector m_data; -}; - - -class color_profile_nclx : public color_profile -{ -public: - color_profile_nclx() { set_default(); } - - uint32_t get_type() const override { return fourcc("nclx"); } - - std::string dump(Indent&) const override; - - Error parse(BitstreamRange& range); - - Error write(StreamWriter& writer) const override; - - uint16_t get_colour_primaries() const { return m_colour_primaries; } - - uint16_t get_transfer_characteristics() const { return m_transfer_characteristics; } - - uint16_t get_matrix_coefficients() const { return m_matrix_coefficients; } - - bool get_full_range_flag() const { return m_full_range_flag; } - - void set_colour_primaries(uint16_t primaries) { m_colour_primaries = primaries; } - - void set_transfer_characteristics(uint16_t characteristics) { m_transfer_characteristics = characteristics; } - - void set_matrix_coefficients(uint16_t coefficients) { m_matrix_coefficients = coefficients; } - - void set_full_range_flag(bool full_range) { m_full_range_flag = full_range; } - - void set_default(); - - void set_undefined(); - - Error get_nclx_color_profile(struct heif_color_profile_nclx** out_data) const; - - static struct heif_color_profile_nclx* alloc_nclx_color_profile(); - - static void free_nclx_color_profile(struct heif_color_profile_nclx* profile); - - void set_from_heif_color_profile_nclx(const struct heif_color_profile_nclx* nclx); - -private: - uint16_t m_colour_primaries = heif_color_primaries_unspecified; - uint16_t m_transfer_characteristics = heif_transfer_characteristic_unspecified; - uint16_t m_matrix_coefficients = heif_matrix_coefficients_unspecified; - bool m_full_range_flag = true; -}; - - -class Box_colr : public Box -{ -public: - Box_colr() - { - set_short_type(fourcc("colr")); - } - - std::string dump(Indent&) const override; - - uint32_t get_color_profile_type() const { return m_color_profile->get_type(); } - - const std::shared_ptr& get_color_profile() const { return m_color_profile; } - - void set_color_profile(const std::shared_ptr& prof) { m_color_profile = prof; } - - - Error write(StreamWriter& writer) const override; - -protected: - Error parse(BitstreamRange& range) override; - -private: - std::shared_ptr m_color_profile; -}; - - /** * User Description property. * diff --git a/libheif/file.h b/libheif/file.h index d812bd09e5..cad841f9ba 100644 --- a/libheif/file.h +++ b/libheif/file.h @@ -24,6 +24,7 @@ #include "avif.h" #include "box.h" #include "hevc.h" +#include "nclx.h" #include #include diff --git a/libheif/nclx.cc b/libheif/nclx.cc index 2869e0ee43..24aa17a123 100644 --- a/libheif/nclx.cc +++ b/libheif/nclx.cc @@ -21,6 +21,12 @@ #include "nclx.h" +#include +#include +#include +#include +#include + primaries::primaries(float gx, float gy, float bx, float by, float rx, float ry, float wx, float wy) { @@ -209,3 +215,245 @@ RGB_to_YCbCr_coefficients RGB_to_YCbCr_coefficients::defaults() return coeffs; } + +Error color_profile_nclx::parse(BitstreamRange& range) +{ + StreamReader::grow_status status; + status = range.wait_for_available_bytes(7); + if (status != StreamReader::size_reached) { + // TODO: return recoverable error at timeout + return Error(heif_error_Invalid_input, + heif_suberror_End_of_data); + } + + m_colour_primaries = range.read16(); + m_transfer_characteristics = range.read16(); + m_matrix_coefficients = range.read16(); + m_full_range_flag = (range.read8() & 0x80 ? true : false); + + return Error::Ok; +} + +Error color_profile_nclx::get_nclx_color_profile(struct heif_color_profile_nclx** out_data) const +{ + *out_data = nullptr; + + struct heif_color_profile_nclx* nclx = alloc_nclx_color_profile(); + + if (nclx == nullptr) { + return Error(heif_error_Memory_allocation_error, + heif_suberror_Unspecified); + } + + struct heif_error err; + + nclx->version = 1; + + err = heif_nclx_color_profile_set_color_primaries(nclx, get_colour_primaries()); + if (err.code) { + free_nclx_color_profile(nclx); + return {err.code, err.subcode}; + } + + err = heif_nclx_color_profile_set_transfer_characteristics(nclx, get_transfer_characteristics()); + if (err.code) { + free_nclx_color_profile(nclx); + return {err.code, err.subcode}; + } + + err = heif_nclx_color_profile_set_matrix_coefficients(nclx, get_matrix_coefficients()); + if (err.code) { + free_nclx_color_profile(nclx); + return {err.code, err.subcode}; + } + + nclx->full_range_flag = get_full_range_flag(); + + // fill color primaries + + auto primaries = ::get_colour_primaries(nclx->color_primaries); + + nclx->color_primary_red_x = primaries.redX; + nclx->color_primary_red_y = primaries.redY; + nclx->color_primary_green_x = primaries.greenX; + nclx->color_primary_green_y = primaries.greenY; + nclx->color_primary_blue_x = primaries.blueX; + nclx->color_primary_blue_y = primaries.blueY; + nclx->color_primary_white_x = primaries.whiteX; + nclx->color_primary_white_y = primaries.whiteY; + + *out_data = nclx; + + return Error::Ok; +} + + +struct heif_color_profile_nclx* color_profile_nclx::alloc_nclx_color_profile() +{ + auto profile = (heif_color_profile_nclx*) malloc(sizeof(struct heif_color_profile_nclx)); + + if (profile) { + profile->version = 1; + profile->color_primaries = heif_color_primaries_unspecified; + profile->transfer_characteristics = heif_transfer_characteristic_unspecified; + profile->matrix_coefficients = heif_matrix_coefficients_ITU_R_BT_601_6; + profile->full_range_flag = true; + } + + return profile; +} + + +void color_profile_nclx::free_nclx_color_profile(struct heif_color_profile_nclx* profile) +{ + free(profile); +} + + +void color_profile_nclx::set_default() +{ + m_colour_primaries = 2; + m_transfer_characteristics = 2; + m_matrix_coefficients = 6; + m_full_range_flag = true; +} + + +void color_profile_nclx::set_undefined() +{ + m_colour_primaries = 2; + m_transfer_characteristics = 2; + m_matrix_coefficients = 2; + m_full_range_flag = true; +} + + +void color_profile_nclx::set_from_heif_color_profile_nclx(const struct heif_color_profile_nclx* nclx) +{ + if (nclx) { + m_colour_primaries = nclx->color_primaries; + m_transfer_characteristics = nclx->transfer_characteristics; + m_matrix_coefficients = nclx->matrix_coefficients; + m_full_range_flag = nclx->full_range_flag; + } +} + + +Error Box_colr::parse(BitstreamRange& range) +{ + StreamReader::grow_status status; + uint32_t colour_type = range.read32(); + + if (colour_type == fourcc("nclx")) { + auto color_profile = std::make_shared(); + m_color_profile = color_profile; + Error err = color_profile->parse(range); + if (err) { + return err; + } + } + else if (colour_type == fourcc("prof") || + colour_type == fourcc("rICC")) { + uint64_t profile_size_64 = get_box_size() - get_header_size() - 4; + if (profile_size_64 > std::numeric_limits::max()) { + return Error(heif_error_Invalid_input, heif_suberror_Security_limit_exceeded, "Color profile exceeds maximum supported size"); + } + + size_t profile_size = static_cast(profile_size_64); + + status = range.wait_for_available_bytes(profile_size); + if (status != StreamReader::size_reached) { + // TODO: return recoverable error at timeout + return Error(heif_error_Invalid_input, + heif_suberror_End_of_data); + } + + std::vector rawData(profile_size); + for (size_t i = 0; i < profile_size; i++) { + rawData[i] = range.read8(); + } + + m_color_profile = std::make_shared(colour_type, rawData); + } + else { + return Error(heif_error_Invalid_input, + heif_suberror_Unknown_color_profile_type); + } + + return range.get_error(); +} + + +std::string Box_colr::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << Box::dump(indent); + + if (m_color_profile) { + sstr << indent << "colour_type: " << to_fourcc(get_color_profile_type()) << "\n"; + sstr << m_color_profile->dump(indent); + } + else { + sstr << indent << "colour_type: ---\n"; + sstr << "no color profile\n"; + } + + return sstr.str(); +} + + +std::string color_profile_raw::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << indent << "profile size: " << m_data.size() << "\n"; + return sstr.str(); +} + + +std::string color_profile_nclx::dump(Indent& indent) const +{ + std::ostringstream sstr; + sstr << indent << "colour_primaries: " << m_colour_primaries << "\n" + << indent << "transfer_characteristics: " << m_transfer_characteristics << "\n" + << indent << "matrix_coefficients: " << m_matrix_coefficients << "\n" + << indent << "full_range_flag: " << m_full_range_flag << "\n"; + return sstr.str(); +} + + +Error color_profile_nclx::write(StreamWriter& writer) const +{ + writer.write16(m_colour_primaries); + writer.write16(m_transfer_characteristics); + writer.write16(m_matrix_coefficients); + writer.write8(m_full_range_flag ? 0x80 : 0x00); + + return Error::Ok; +} + +Error color_profile_raw::write(StreamWriter& writer) const +{ + writer.write(m_data); + + return Error::Ok; +} + +Error Box_colr::write(StreamWriter& writer) const +{ + size_t box_start = reserve_box_header_space(writer); + + assert(m_color_profile); + + writer.write32(m_color_profile->get_type()); + + Error err = m_color_profile->write(writer); + if (err) { + return err; + } + + prepend_header(writer, box_start); + + return Error::Ok; +} + + diff --git a/libheif/nclx.h b/libheif/nclx.h index 1a38c9a844..10a92931e1 100644 --- a/libheif/nclx.h +++ b/libheif/nclx.h @@ -21,7 +21,12 @@ #ifndef LIBHEIF_NCLX_H #define LIBHEIF_NCLX_H +#include "box.h" + #include +#include +#include +#include struct primaries { @@ -81,4 +86,112 @@ RGB_to_YCbCr_coefficients get_RGB_to_YCbCr_coefficients(uint16_t matrix_coeffici // bool get_full_range_flag() const {return m_full_range_flag;} +class color_profile +{ +public: + virtual ~color_profile() = default; + + virtual uint32_t get_type() const = 0; + + virtual std::string dump(Indent&) const = 0; + + virtual Error write(StreamWriter& writer) const = 0; +}; + +class color_profile_raw : public color_profile +{ +public: + color_profile_raw(uint32_t type, const std::vector& data) + : m_type(type), m_data(data) {} + + uint32_t get_type() const override { return m_type; } + + const std::vector& get_data() const { return m_data; } + + std::string dump(Indent&) const override; + + Error write(StreamWriter& writer) const override; + +private: + uint32_t m_type; + std::vector m_data; +}; + + +class color_profile_nclx : public color_profile +{ +public: + color_profile_nclx() { set_default(); } + + uint32_t get_type() const override { return fourcc("nclx"); } + + std::string dump(Indent&) const override; + + Error parse(BitstreamRange& range); + + Error write(StreamWriter& writer) const override; + + uint16_t get_colour_primaries() const { return m_colour_primaries; } + + uint16_t get_transfer_characteristics() const { return m_transfer_characteristics; } + + uint16_t get_matrix_coefficients() const { return m_matrix_coefficients; } + + bool get_full_range_flag() const { return m_full_range_flag; } + + void set_colour_primaries(uint16_t primaries) { m_colour_primaries = primaries; } + + void set_transfer_characteristics(uint16_t characteristics) { m_transfer_characteristics = characteristics; } + + void set_matrix_coefficients(uint16_t coefficients) { m_matrix_coefficients = coefficients; } + + void set_full_range_flag(bool full_range) { m_full_range_flag = full_range; } + + void set_default(); + + void set_undefined(); + + Error get_nclx_color_profile(struct heif_color_profile_nclx** out_data) const; + + static struct heif_color_profile_nclx* alloc_nclx_color_profile(); + + static void free_nclx_color_profile(struct heif_color_profile_nclx* profile); + + void set_from_heif_color_profile_nclx(const struct heif_color_profile_nclx* nclx); + +private: + uint16_t m_colour_primaries = heif_color_primaries_unspecified; + uint16_t m_transfer_characteristics = heif_transfer_characteristic_unspecified; + uint16_t m_matrix_coefficients = heif_matrix_coefficients_unspecified; + bool m_full_range_flag = true; +}; + + +class Box_colr : public Box +{ +public: + Box_colr() + { + set_short_type(fourcc("colr")); + } + + std::string dump(Indent&) const override; + + uint32_t get_color_profile_type() const { return m_color_profile->get_type(); } + + const std::shared_ptr& get_color_profile() const { return m_color_profile; } + + void set_color_profile(const std::shared_ptr& prof) { m_color_profile = prof; } + + + Error write(StreamWriter& writer) const override; + +protected: + Error parse(BitstreamRange& range) override; + +private: + std::shared_ptr m_color_profile; +}; + + #endif //LIBHEIF_NCLX_H diff --git a/libheif/pixelimage.h b/libheif/pixelimage.h index 7e4c5e385a..79b86c8f4a 100644 --- a/libheif/pixelimage.h +++ b/libheif/pixelimage.h @@ -24,7 +24,7 @@ #include "heif.h" #include "error.h" -#include "box.h" // only for color_profile, TODO: maybe move the color_profiles to its own header +#include "nclx.h" #include #include