Skip to content

Commit

Permalink
Add Timestamp Properties
Browse files Browse the repository at this point in the history
  • Loading branch information
dukesook committed Nov 5, 2023
1 parent 9700634 commit 2b868d7
Show file tree
Hide file tree
Showing 4 changed files with 383 additions and 3 deletions.
71 changes: 71 additions & 0 deletions libheif/box.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
110 changes: 109 additions & 1 deletion libheif/box.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<float>::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
159 changes: 159 additions & 0 deletions libheif/heif_properties.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<Box_taic>();


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<Box_taic>(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<Box_itai>();


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<Box_itai>(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;
}

Loading

0 comments on commit 2b868d7

Please sign in to comment.