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:
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.
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.
Valid.
The klv_value contains data of the appropriate type. For example, if the
keyof the klv_packet is the ST0601 universal key, a validvaluewould 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_blockand 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.
Return the number of yet-unprocessed frames.
-
virtual bool check_configuration(vital::config_block_sptr config) const override
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
nullptrif this format does not have such a checksum.
-
virtual std::string description_() const override
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_valueto 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
valuecannot 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_infofor 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
valuetoos.
-
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
nullptrif 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
nullptrif 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
nullptrif 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.
-
explicit klv_data_format(klv_length_constraints const &length_constraints = {})
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.
-
virtual vital::metadata *clone() const override
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::stringno 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.
-
virtual std::string description_() const override
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 (==), andstd::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_valueis a relatively low-context data type in the KLV hierachy. The type of aklv_valuedoes 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 fromklv_data_formatdeal 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.
-
vital::any to_any() const
Create an
anyobject with a copy of this value.
-
bool empty() const noexcept
Check if the object contains no value.
-
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>
class internal_ : public kwiver::arrows::klv::klv_value::internal_base
-
class internal_base
Subclassed by kwiver::arrows::klv::klv_value::internal_< T >
-
klv_value()
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
packetis too large to fit inlengthbytes.