KLV

The KLV (Key-Length-Value) arrow provides decoding and encoding capabilities for many common MISB standards. This arrow does not handle the muxing or demuxing of KLV streams in specific media formats; that is handled in the FFmpeg arrow. This arrow can be built by enabling the KWIVER_ENABLE_KLV CMake flag.

The guiding principles for this implementation are as follows:

  • Compliance. The KLV arrow attempts to comply with the MISB standards.

  • Completeness. The KLV arrow attempts to handle all tags, corner cases, and obscure features in any standard it implements.

  • Robustness. The KLV arrow should not crash when erroneous data is encountered, but instead attempt to parse as much as it can. Any sections of unparseable KLV data are still retained in the form of raw bytes.

  • Editability. Incoming KLV data is decoded into an intermediate software representation composed of standard C++ types and custom classes, allowing it to be manipulated independently of the encode / decode process.

  • Losslessness. Any valid data decoded by the KLV arrow can be re-encoded in an equivalent form, with “equivalent” indicating no useful information is lost, not byte-for-byte equality.

  • Efficiency. Encoded KLV data is quite small compared to a typical accompanying video stream. The KLV arrow attempts to keep the compute and memory cost of processing KLV to negligable levels.

  • Recency. The KLV arrow keeps up to date with the latest versions of the MISB standards and does not attempt to offer perfect backwards-compatibility or compliance with outdated versions. However, support for some deprecated features may be kept around if enough data that uses those features exists in the wild, or if doing so is trivial.

While KWIVER may log various warnings and errors, this implementation does not attempt to recognize all nonconformance with MISB standards that may be present in input data, nor does it always prevent the encoding of nonconformant data. This latter policy allows the system to process slightly nonconformant data as may be present in the wild, but it places the responsibility on the user to ensure that the data and encoding settings they pass in are standard-conformant.

How to Use

The easiest way to leverage the capabilities of the KLV arrow is through the dump-klv command-line applet. The default printed output and csv exporter give an interpreted summary of commonly useful metadata fields (latitude/longitude, etc.) extracted mostly from ST0601, while the klv-json exporter will give a complete tag-by-tag report across all standards.

To manipulate KLV programmatically, parsed klv_packets may be obtained from an ffmpeg_video_input through the frame_metadata() method. Each of the returned vital::metadata objects which successfully dynamic_casts to klv_metadata contains one frame of klv_packets from one KLV stream in the source media.

Starting with a klv_packet at the root, parsed KLV data is stored as a hierachy. Along this hierachy, some data (e.g. klv_packet.key) is stored directly, while some (e.g. klv_packet.value) is stored in a klv_value container. A klv_value can be thought of as being in three basic states:

  1. Empty.

There is no data for this field. In a local set, this indicates a zero-length element (ZLE), relevant for Report-on-Change behavior. In other contexts, it may simply indicate an omitted optional field.

  1. Invalid.

There is data in this field, but we cannot parse it. Either it is irrecoverably nonconformant, or we have not yet implemented the relevant standard. In either case, the byte sequence is stored in a klv_blob object within the klv_value. That byte sequence can then be written back out verbatim to prevent data loss.

  1. Valid.

The klv_value contains data of the appropriate type. For example, if the key of the klv_packet is the ST0601 universal key, a valid value would contain an object of type klv_local_set.

Warning

When processing parsed KLV, developers should make sure to handle cases where a klv_value is not valid.

The transformations between encoded KLV and KWIVER’s in-memory data structures are organized via klv_data_format classes, such as klv_string_format and klv_0601_local_set_format. Each format implements reading and writing a particular type of data, and may invoke other formats internally. In addition to read() and write() methods, each format has a length_of() method, which allows the writer to preallocate the exact number of bytes needed to hold the encoded KLV data.

However, in most cases users will be reading from or writing to a video file, in which case the video_input or video_output will handle the KLV packet decoding and encoding internally. To read or write packets independently of a video stream, see the klv_read_packet and klv_write_packet functions. These will automatically select the correct format to handle the data given to them.

Algorithm Implementations

apply_child_klv

class apply_child_klv : public kwiver::vital::algo::metadata_filter

Applies KLV amend and segment sets.

update_klv

class update_klv : public kwiver::vital::algo::buffered_metadata_filter

Attempts to encode vital metadata fields into KLV.

This is very much not comprehensive or lossless. Direct manipulation of KLV should be preferred for precision.

Warning

Only feed this filter a single video, in frame order. Past metadata fed to it is used in the algorithm.

Public Functions

virtual bool check_configuration(vital::config_block_sptr config) const override

Check that the algorithm’s configuration config_block is valid

This checks solely within the provided config_block and not against the current state of the instance. This isn’t static for inheritance reasons.

Parameters:

config – The config block to check configuration of.

Returns:

true if the configuration check passed and false if it didn’t.

virtual size_t send(vital::metadata_vector const &input_metadata, vital::image_container_scptr const &input_image) override

Provide one frame of metadata to the filter.

This method implements the filtering operation, which may delay producing output until more frames are sent. The method does not modify the metadata in place.

Parameters:
  • input_metadata – Metadata to filter.

  • input_image – Image associated with the metadata (may be null).

Returns:

The number of frames of output available.

virtual vital::metadata_vector receive() override

Return one frame of processed metadata.

Throws:

std::runtime_error – If no output is available.

virtual size_t flush() override

Force the buffer to process all sent input immediately.

This method forces a wipe of all internal input buffers, ensuring that immediately subsequent calls to unavailable_frames() return zero. This may result in frames being processed in an inferior manner. This method should be called when there is no more input.

virtual size_t available_frames() const override

Return the number of processed frames.

virtual size_t unavailable_frames() const override

Return the number of yet-unprocessed frames.

class impl
struct stream

Other Classes

klv_0601_local_set_format

class klv_0601_local_set_format : public kwiver::arrows::klv::klv_set_format<klv_lds_key>

Interprets data as a metadata substream id.

Interprets data as a ST0601 local set.

Public Functions

virtual std::string description_() const override

Return a textual description of this data format, not mentioning length constraints.

virtual klv_checksum_packet_format const *packet_checksum_format() const override

Return the checksum format for the entire packet, or nullptr if this format does not have such a checksum.

klv_blob

class klv_blob

Structure to hold explicitly uninterpreted bytes.

This wrapper type is used to signify that the bytes it holds were unable to be parsed, likely due to an unsupported field or irrecoverably incorrect formatting. Unparsed bytes are still stored, however, to potentially write them back out later.

klv_data_format

class klv_data_format

Untyped base for all KLV data formats.

This class provides an interface for accessing reading, writing, and printing capabilities for specific formats, as well as implementations of basic methods common to all formats.

Subclassed by kwiver::arrows::klv::klv_data_format_< klv_lengthy< double > >, kwiver::arrows::klv::klv_data_format_< int64_t >, kwiver::arrows::klv::klv_data_format_< klv_lengthy< klv_imap > >, kwiver::arrows::klv::klv_data_format_< Format::data_type::value_type >, kwiver::arrows::klv::klv_data_format_< std::string >, kwiver::arrows::klv::klv_data_format_< klv_set< Key > >, kwiver::arrows::klv::klv_data_format_< klv_blob >, kwiver::arrows::klv::klv_data_format_< klv_uuid >, kwiver::arrows::klv::klv_data_format_< klv_0903_location_pack >, kwiver::arrows::klv::klv_data_format_< klv_0903_velocity_pack >, kwiver::arrows::klv::klv_data_format_< std::vector< Format::data_type > >, kwiver::arrows::klv::klv_data_format_< std::set< Enum > >, kwiver::arrows::klv::klv_data_format_< klv_1303_mdap< klv_imap > >, kwiver::arrows::klv::klv_data_format_< klv_1108_metric_implementer >, kwiver::arrows::klv::klv_data_format_< std::decay_t< T > >, kwiver::arrows::klv::klv_data_format_< bool >, kwiver::arrows::klv::klv_data_format_< uint64_t >, kwiver::arrows::klv::klv_data_format_< T >

Public Functions

explicit klv_data_format(klv_length_constraints const &length_constraints = {})
Parameters:

length_constraints – Constraints determining how many bytes the serialized KLV can be.

virtual klv_value read(klv_read_iter_t &data, size_t length) const = 0

Parse raw bytes into data type; return as klv_value.

Parameters:
  • data – Iterator to input bytes. Will be set to the end of bytes read on success, or back to its original value on failure.

  • length – The length of the value as parsed from the KLV length field.

virtual void write(klv_value const &value, klv_write_iter_t &data, size_t max_length) const = 0

Write klv_value to raw bytes.

Parameters:
  • value – Value to write.

  • data – Iterator to output bytes. Will be set to the end of bytes written.

  • max_length – The maximum number of bytes to write. An exception will be thrown if value cannot be written within that number of bytes.

virtual size_t length_of(klv_value const &value) const = 0

Return the number of bytes required to write value.

Note

The return value does not account for a checksum, if present.

Parameters:

value – Value to check the length of.

virtual std::type_info const &type() const = 0

Return type_info for read / written type.

std::string type_name() const

Return name of read / written type.

virtual std::ostream &print(std::ostream &os, klv_value const &value) const = 0

Print a string representation of value to os.

std::string to_string(klv_value const &value) const

Return a string representation of value.

std::string description() const

Return a textual description of this data format.

virtual klv_checksum_packet_format const *prefix_checksum_format() const

Return the checksum format for the packet key and length only, or nullptr if this format does not have such a checksum.

virtual klv_checksum_packet_format const *payload_checksum_format() const

Return the checksum format for the packet payload only, or nullptr if this format does not have such a checksum.

virtual klv_checksum_packet_format const *packet_checksum_format() const

Return the checksum format for the entire packet, or nullptr if this format does not have such a checksum.

klv_length_constraints const &length_constraints() const

Return the constraints on the length of this format.

void set_length_constraints(klv_length_constraints const &length_constraints)

Set constraints on the length of this format.

klv_metadata

class klv_metadata : public kwiver::vital::metadata

A superset of standard vital metadata which also holds parsed KLV packets and a MISP frame timestamp.

Public Functions

virtual vital::metadata *clone() const override

Create a deep copy of this object.

std::vector<klv_packet> const &klv() const

Return a reference to the contained KLV packets.

std::vector<klv_packet> &klv()

Return a reference to the contained KLV packets.

std::optional<misp_timestamp> const &frame_timestamp() const

Return a reference to the contained MISP frame timestamp.

std::optional<misp_timestamp> &frame_timestamp()

Return a reference to the contained MISP frame timestamp.

klv_packet

struct klv_packet

Top-level KLV packet.

A KLV metadata stream consists of a sequence of these.

klv_string_format

class klv_string_format : public kwiver::arrows::klv::klv_data_format_<std::string>

Interprets data as a string, with awareness of text encoding.

Strings are always represented as std::string no matter the encoding.

Subclassed by kwiver::arrows::klv::klv_ascii_format, kwiver::arrows::klv::klv_utf_16_format, kwiver::arrows::klv::klv_utf_8_format

Public Functions

virtual std::string description_() const override

Return a textual description of this data format, not mentioning length constraints.

klv_value

class klv_value

Type-erased container class for the values of KLV fields.

This class can hold any KLV value, or be empty. Any type to be held in this container must have comparison ( < ), equality ( == ), and std::ostream ( << ) operators defined; these are passed on through this container and allow basic generic operations such as sorting and printing values to be performed without having to know the type of each value at compile time.

Generally a KLV value is expected to be in one of three states: empty, invalid, or valid. An empty value has no type and no data. An invalid value has a type of klv_blob, and contains only raw bytes, usually the result of failure to parse. A valid value has data of some other type which is determined by the context in which the value exists.

klv_value is a relatively low-context data type in the KLV hierachy. The type of a klv_value does not uniquely identify how that value is to be interpreted or serialized; there are a handful of different ways of encoding integers or floating-point numbers into KLV, for example. Classes derived from klv_data_format deal with that next layer of specificity.

Public Functions

klv_value()

Construct an empty object.

template<class T, typename = std::enable_if_t<!std::is_same_v<std::decay_t<T>, klv_value> && !std::is_same_v<std::decay_t<T>, vital::any>>>
klv_value(T &&value)

Move some external type into a new object.

template<class T>
klv_value &operator=(T &&rhs)

Move some external type into this object.

klv_value &swap(klv_value &rhs) noexcept

Swap the contents of this object with rhs.

vital::any to_any() const

Create an any object with a copy of this value.

bool empty() const noexcept

Check if the object contains no value.

bool valid() const noexcept

Check if the object contains a value which is not of type klv_blob.

void clear() noexcept

Remove any existing value.

std::type_info const &type() const noexcept

Return type information for the contained value.

std::string type_name() const noexcept

Return the demangled type name of the contained value.

std::string to_string() const

Return a string representation of the contained value.

template<class T>
T &get()

Return a reference to the contained value of type T.

Throws:

klv_bad_value_cast – If the object does not contain a value of type T.

template<class T>
T const &get() const

< class T > T& get()

< class T > T& get()

template<class T>
T *get_ptr() noexcept

Return a pointer to the contained value of type T, or nullptr on failure.

template<class T>
T const *get_ptr() const noexcept

< class T > T* get_ptr()

< class T > T* get_ptr()

template<class T>
class internal_ : public kwiver::arrows::klv::klv_value::internal_base
class internal_base

Subclassed by kwiver::arrows::klv::klv_value::internal_< T >

Utility Functions

klv_packet_length

size_t kwiver::arrows::klv::klv_packet_length(klv_packet const &packet)

Return the number of bytes required to store the given packet.

Parameters:

packet – KLV packet whose byte length is being queried.

Returns:

Bytes required to store packet.

klv_read_packet

klv_packet kwiver::arrows::klv::klv_read_packet(klv_read_iter_t &data, size_t max_length)

Find and read a KLV packet from a sequence of bytes.

This function will search for a valid UDS key in the given bytes, indicating the beginning of a packet. It will skip any number of non-packet bytes to find the next packet. The value

Parameters:
  • data[inout] Iterator to sequence of uint8_t. Set to end of read bytes on success or if no KLV is found; left as is on error.

  • max_length – Maximum number of bytes to read.

Throws:
  • metadata_buffer_overflow – If more bytes are needed to find or read the next packet.

  • metadata_exception – If the UDS key prefix is found, but it is attached to an invalid key.

Returns:

Packet read in from data.

klv_write_packet

void kwiver::arrows::klv::klv_write_packet(klv_packet const &packet, klv_write_iter_t &data, size_t max_length)

Write a KLV packet to a sequence of bytes.

Parameters:
  • packet – KLV packet to write.

  • data[inout] Writeable iterator to sequence of uint8_t. Set to end of written bytes on success, left as is on error.

  • max_length – Maximum number of bytes to write.

Throws:

metadata_buffer_overflow – When packet is too large to fit in length bytes.