From 2b868d74a7f89354ad1aad2974fb00bd2a6a6883 Mon Sep 17 00:00:00 2001 From: dukesook Date: Sat, 4 Nov 2023 00:59:26 +0000 Subject: [PATCH] Add Timestamp Properties --- libheif/box.cc | 71 +++++++++++++++++ libheif/box.h | 110 ++++++++++++++++++++++++- libheif/heif_properties.cc | 159 +++++++++++++++++++++++++++++++++++++ libheif/heif_properties.h | 46 ++++++++++- 4 files changed, 383 insertions(+), 3 deletions(-) diff --git a/libheif/box.cc b/libheif/box.cc index 4c0a85afe4..5c4897da26 100644 --- a/libheif/box.cc +++ b/libheif/box.cc @@ -3087,3 +3087,74 @@ Error Box_udes::write(StreamWriter& writer) const prepend_header(writer, box_start); return Error::Ok; } + + + +std::string Box_taic::dump(Indent& indent) const { + std::ostringstream sstr; + sstr << Box::dump(indent); + sstr << indent << "time_uncertainty: " << m_time_uncertainty << "\n"; + sstr << indent << "correction_offset: " << m_correction_offset << "\n"; + sstr << indent << "clock_drift_rate: " << m_clock_drift_rate << "\n"; + sstr << indent << "clock_source: " << m_clock_source << "\n"; + return sstr.str(); +} + +Error Box_taic::write(StreamWriter& writer) const { + uint32_t cdr_uint32; + std::memcpy(&cdr_uint32, &m_clock_drift_rate, sizeof(float)); + + size_t box_start = reserve_box_header_space(writer); + writer.write64(m_time_uncertainty); + writer.write64(m_correction_offset); + writer.write32(cdr_uint32); + writer.write8(m_clock_source); + + prepend_header(writer, box_start); + + return Error::Ok; +} + +Error Box_taic::parse(BitstreamRange& range) { + parse_full_box_header(range); + + uint64_t high = range.read32(); + uint64_t low = range.read32(); + m_time_uncertainty = (high << 32) | low; + + high = range.read32(); + low = range.read32(); + m_correction_offset = (high << 32) | low; + + m_clock_drift_rate = (float) range.read32(); + m_clock_source = range.read8(); + return range.get_error(); +} + +std::string Box_itai::dump(Indent& indent) const { + std::ostringstream sstr; + sstr << Box::dump(indent); + sstr << indent << "TAI_timestamp: " << m_TAI_timestamp << "\n"; + sstr << indent << "status_bits: " << m_status_bits << "\n"; + return sstr.str(); +} + +Error Box_itai::write(StreamWriter& writer) const { + size_t box_start = reserve_box_header_space(writer); + writer.write64(m_TAI_timestamp); + writer.write8(m_status_bits); + + prepend_header(writer, box_start); + return Error::Ok; +} + +Error Box_itai::parse(BitstreamRange& range) { + parse_full_box_header(range); + + uint64_t high = range.read32(); + uint64_t low = range.read32(); + m_TAI_timestamp = (high << 32) | low; + + m_status_bits = range.read8(); + return range.get_error(); +} \ No newline at end of file diff --git a/libheif/box.h b/libheif/box.h index 3a651f6ea9..6fdee58794 100644 --- a/libheif/box.h +++ b/libheif/box.h @@ -1058,4 +1058,112 @@ class Box_udes : public FullBox std::string m_tags; }; -#endif + + +class Box_taic : public FullBox +{ +public: + Box_taic() + { + set_short_type(fourcc("taic")); + } + + std::string dump(Indent&) const override; + + Error write(StreamWriter& writer) const override; + + /** + * time_uncertainty. + * + * The standard deviation measurement uncertainty in nanoseconds + * for the timestamp generation process. + */ + void set_time_uncertainty(uint64_t time_uncertainty) { m_time_uncertainty = time_uncertainty;} + + /** + * correction_offset. + * + * The difference in nanoseconds between the clock’s reported + * timestamp and true time value of the measurement event. + */ + void set_correction_offset(int64_t correction_offset) { m_correction_offset = correction_offset; } + + /** + * clock_drift_rate. + * + * The difference between the synchronized and unsynchronized + * time, over a period of one second. + */ + void set_clock_drift_rate(float clock_drift_rate) { m_clock_drift_rate = clock_drift_rate; } + + /** + * clock_source. + * + * 0 = Clock type is unkown + * 1 = The clock does not synchronize to an atomic source of absolute TAI time + * 2 = The clock can synchronize to an atomic source of absolute TAI time + */ + void set_clock_source(uint8_t clock_source) { m_clock_source = clock_source; } + + uint64_t get_time_uncertainty() const { return m_time_uncertainty; } + + int64_t get_correction_offset() const { return m_correction_offset; } + + float get_clock_drift_rate() const { return m_clock_drift_rate; } + + uint8_t get_clock_source() const { return m_clock_source; } + +protected: + Error parse(BitstreamRange& range) override; + +private: + // Initialized to "unknown" + uint64_t m_time_uncertainty = 0xFFFFFFFFFFFFFFFF; + int64_t m_correction_offset = 0x7FFFFFFFFFFFFFFF; + float m_clock_drift_rate = std::numeric_limits::quiet_NaN(); + uint8_t m_clock_source = 0; +}; + + +class Box_itai : public FullBox +{ +public: + Box_itai() + { + set_short_type(fourcc("itai")); + } + + std::string dump(Indent&) const override; + + Error write(StreamWriter& writer) const override; + + /** + * timestamp. + * + * The number of nanoseconds since the TAI epoch of 1958-01-01T00:00:00.0Z. + */ + void set_TAI_timestamp(uint64_t timestamp) { m_TAI_timestamp = timestamp; } + + /** + * status_bits. + * + * Bit 0: Synchronization Status (0=unsynchronized, 1=synchronized) + * Bit 1: Timestamp validity (0=invalid, 1=valid) + * Bits 2-7: Reserved + */ + void set_status_bits(uint8_t status_bits) { m_status_bits = status_bits; } + + uint64_t get_TAI_timestamp() const { return m_TAI_timestamp; } + + uint8_t get_status_bits() const { return m_status_bits; } + +protected: + Error parse(BitstreamRange& range) override; + +private: + // Initialized to "unknown" + uint64_t m_TAI_timestamp = 0xFFFFFFFFFFFFFFFF; + uint8_t m_status_bits = 0; +}; + +#endif \ No newline at end of file diff --git a/libheif/heif_properties.cc b/libheif/heif_properties.cc index d73cea41ca..90ab9284ed 100644 --- a/libheif/heif_properties.cc +++ b/libheif/heif_properties.cc @@ -300,3 +300,162 @@ void heif_property_user_description_release(struct heif_property_user_descriptio delete udes; } + +struct heif_error heif_property_set_clock_info(const struct heif_context* context, + heif_item_id itemId, + const uint64_t* time_uncertainty, + const int64_t* correction_offset, + const float* clock_drift_rate, + const uint8_t* clock_source, + heif_property_id* out_propertyId) +{ + if (!context) { + return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; + } + + // TODO - if the property already exists, we should update it instead of creating a new one. + auto taic = std::make_shared(); + + + if (time_uncertainty != nullptr) { + taic->set_time_uncertainty(*time_uncertainty); + } + if (correction_offset != nullptr) { + taic->set_correction_offset(*correction_offset); + } + if (clock_drift_rate != nullptr) { + taic->set_clock_drift_rate(*clock_drift_rate); + } + if (clock_source != nullptr) { + taic->set_clock_source(*clock_source); + } + + bool essential = false; + heif_property_id id = context->context->add_property(itemId, taic, essential); + + + + if (out_propertyId) { + *out_propertyId = id; + } + + return heif_error_success; +} + +struct heif_error heif_property_get_clock_info(const struct heif_context* context, + heif_item_id itemId, + uint64_t* out_time_uncertainty, + int64_t* out_correction_offset, + float* out_clock_drift_rate, + uint8_t* out_clock_source) +{ + if (!context) { + return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; + } + + auto file = context->context->get_heif_file(); + + // TODO - use a function to get the taic instead of duplicating code. + auto ipco = file->get_ipco_box(); + auto impa = file->get_ipma_box(); + auto prop = ipco->get_property_for_item_ID(itemId, impa, fourcc("taic")); + auto taic = std::dynamic_pointer_cast(prop); + if (!taic) { + return {heif_error_Usage_error, heif_suberror_Invalid_property, "Clock info property not found"}; + } + + // TODO - if the value is unknown, return a nullptr. + if (out_time_uncertainty) { + *out_time_uncertainty = taic->get_time_uncertainty(); + } + if (out_correction_offset) { + *out_correction_offset = taic->get_correction_offset(); + } + if (out_clock_drift_rate) { + *out_clock_drift_rate = taic->get_clock_drift_rate(); + } + if (out_clock_source) { + *out_clock_source = taic->get_clock_source(); + } + + + return heif_error_success; + +} + +struct heif_error heif_property_set_tai_timestamp(const struct heif_context* context, + heif_item_id itemId, + const uint64_t* tai_timestamp, + const uint8_t* status_bits, + heif_property_id* out_propertyId) +{ + if (!context) { + return {heif_error_Usage_error, heif_suberror_Null_pointer_argument, "NULL passed"}; + } + + // TODO - if the property already exists, we should update it instead of creating a new one. + auto itai = std::make_shared(); + + + if (tai_timestamp != nullptr) { + itai->set_TAI_timestamp(*tai_timestamp); + } + if (status_bits != nullptr) { + itai->set_status_bits(*status_bits); + } + + bool essential = false; + heif_property_id id = context->context->add_property(itemId, itai, essential); + + // A taic box shall be present point to the same item as the itai box. + // TODO - use a function to get the taic instead of duplicating code. + heif_error err; + auto file = context->context->get_heif_file(); + auto ipco = file->get_ipco_box(); + auto impa = file->get_ipma_box(); + auto taic = ipco->get_property_for_item_ID(itemId, impa, fourcc("taic")); + if (!taic) { + err = heif_property_set_clock_info(context, itemId, nullptr, nullptr, nullptr, nullptr, nullptr); + if (err.code != heif_error_Ok) { + return err; + } + } + + + if (out_propertyId) { + *out_propertyId = id; + } + + return heif_error_success; +} + +struct heif_error heif_property_get_tai_timestamp(const struct heif_context* context, + heif_item_id itemId, + uint64_t* out_tai_timestamp, + uint8_t* out_status_bits) +{ + if (!context) { + return {heif_error_Usage_error, heif_suberror_Invalid_parameter_value, "NULL passed"}; + } + + auto file = context->context->get_heif_file(); + + // TODO - use a function to get the taic instead of duplicating code. + auto ipco = file->get_ipco_box(); + auto impa = file->get_ipma_box(); + auto prop = ipco->get_property_for_item_ID(itemId, impa, fourcc("itai")); + auto itai = std::dynamic_pointer_cast(prop); + if (!itai) { + return {heif_error_Usage_error, heif_suberror_Invalid_property, "Timestamp property not found"}; + } + + if (out_tai_timestamp) { + *out_tai_timestamp = itai->get_TAI_timestamp(); + } + if (out_status_bits) { + *out_status_bits = itai->get_status_bits(); + } + + return heif_error_success; +} + diff --git a/libheif/heif_properties.h b/libheif/heif_properties.h index 4ed15c8a90..b882ca1943 100644 --- a/libheif/heif_properties.h +++ b/libheif/heif_properties.h @@ -37,7 +37,9 @@ enum heif_item_property_type heif_item_property_type_transform_mirror = heif_fourcc('i', 'm', 'i', 'r'), heif_item_property_type_transform_rotation = heif_fourcc('i', 'r', 'o', 't'), heif_item_property_type_transform_crop = heif_fourcc('c', 'l', 'a', 'p'), - heif_item_property_type_image_size = heif_fourcc('i', 's', 'p', 'e') + heif_item_property_type_image_size = heif_fourcc('i', 's', 'p', 'e'), + heif_item_property_type_tai_clock_info = heif_fourcc('t', 'a', 'i', 'c'), + heif_item_property_type_tai_timestamp = heif_fourcc('i', 't', 'a', 'i'), }; // Get the heif_property_id for a heif_item_id. @@ -131,8 +133,48 @@ void heif_item_get_property_transform_crop_borders(const struct heif_context* co int image_width, int image_height, int* left, int* top, int* right, int* bottom); + +// ========================= Timestamps ========================= +// Creates a clock info property if it doesn't already exist. +// A null clock argument implies that the value is unknown. +// Example: +// int64_t offset = 0x100; +// heif_property_id propertyId; +// heif_property_set_clock_info(ctx, itemId, nullptr, &offset, nullptr, nullptr, &propertyId); +LIBHEIF_API +struct heif_error heif_property_set_clock_info(const struct heif_context* ctx, + heif_item_id itemId, + const uint64_t* time_uncertainty, + const int64_t* correction_offset, + const float* clock_drift_rate, + const uint8_t* clock_source, + heif_property_id* out_propertyId); + +// If the value is unknown, the pointer is set to nullptr. +// A null clock argument implies that the value is unknown. +LIBHEIF_API +struct heif_error heif_property_get_clock_info(const struct heif_context* ctx, + heif_item_id itemId, + uint64_t* out_time_uncertainty, + int64_t* out_correction_offset, + float* out_clock_drift_rate, + uint8_t* out_clock_source); + +struct heif_error heif_property_set_tai_timestamp(const struct heif_context* ctx, + heif_item_id itemId, + const uint64_t* tai_timestamp, + const uint8_t* status_bits, + heif_property_id* out_propertyId); + +LIBHEIF_API +struct heif_error heif_property_get_tai_timestamp(const struct heif_context* ctx, + heif_item_id itemId, + uint64_t* out_tai_timestamp, + uint8_t* out_status_bits); + + #ifdef __cplusplus } #endif -#endif +#endif \ No newline at end of file