Image Support
This page describes how VRS handles content blocks using the Datalayout Conventions.
Prerequisite: Understanding how RecordFormat
works.
Image content blocks can be any of the following different subtypes:
image/raw
The image is stored as a buffer of raw pixels, which might a compacted pixel format such as RAW10, but not compressed using aPixelFormat
.
The exact list of pixel formats grows over time.image/png
,image/jpg
,image/jxl
, andimage/custom_codec
The image is compressed using png, jpeg, jpeg-xl, or a custom codec. The payload of the image content block contains exactly what regular file of that image type would contain
Note that when using a custom codec, you must specify a codec name, or the image content block will be reported as unsupported on read.image/video
The image is compressed according to the spec of a video codec standard, such as H.264 or H.265.
Note that the list of supported video codecs is deliberately not part of the spec, but rather, is meant to be implementation dependent, by an extension. VRS Open Source provides the vrs/xprs module to interface with ffmpeg and expose the codecs it was built with.
Additional Image Properties
To be complete, image content blocks, in particular image/raw
content blocks, may require the following complementary properties for an image:
- Resolution
- Stride
- Pixel format
Without these properties, image/raw
images cannot be interpreted safely. These properties can be provided in two distinct ways:
- The image properties can be part of the
RecordFormat
description itself, meaning that the records using thatRecordFormat
will all be using the exact same pixel format.
The content block description might look like this:image/raw/640x480/pixel=grey8
image/raw/640x480/pixel=yuv_i420_split/stride=640
- The image properties can be provided in the datalayout content block found either before the
image
content block in the same record, or in the stream’s last configuration record. When using this method, the properties must be specified using the Datalayout Conventions, which are merely a collection of data types and labels to use in yourDataLayout
to specify properties.
Whichever method you use, VRS collects the image properties for you, so that the RecordFormatStreamPlayer::onImageRead()
callback can directly provide a content block object that you can query for the image format, regardless of how or where it was specified.
Specifying Additional Image Properties
For image/raw
and image/video
images, use a vrs::DataLayoutConventions::ImageSpec
to describe your image in a configuration record, or in a DataLayout
block just before the image content block of your data records.
The DataLayout
fields are:
DataPieceValue<ImageSpecType> width{kImageWidth};
DataPieceValue<ImageSpecType> height{kImageHeight};
DataPieceValue<ImageSpecType> stride{kImageStride};
DataPieceValue<ImageSpecType> stride2{kImageStride2};
DataPieceEnum<PixelFormat, ImageSpecType> pixelFormat{kImagePixelFormat};
// For image/custom_codec and image/video only
DataPieceString codecName{kImageCodecName}; // required
DataPieceValue<ImageSpecType> codecQuality{kImageCodecQuality}; // optional
For image/video
images, specify the keyframe timestamp and the keyframe index, using the following DataLayout
fields:
DataPieceValue<double> keyFrameTimestamp{kImageKeyFrameTimeStamp};
DataPieceValue<ImageSpecType> keyFrameIndex{kImageKeyFrameIndex};
Image Format Differences
image/raw
Without the following properties, image/raw
image content blocks can not be interpreted:
- Required properties: width, height, pixel format.
- Optional properties: stride, stride2.
When properties are provided using the Datalayout Conventions, all additional properties must be provided by a single DataLayout
structure in one record. For example, you must not put the pixel format in the configuration record, and then put the image dimensions in the data record. You must either put both the pixel format and the image dimensions in a configuration record's DataLayout
, or you must put them both in a DataLayout
just before the image content block.
image/png
, image/jpg
and image/jxl
png, jpg, and jxl payloads are exactly the same as png, jpg, and jxl files, which are fully self-described. Therefore, when those image formats are used, properties specified using the Datalayout Conventions are ignored by VRS.
image/custom_codec
- Required properties: codec name.
- Optional properties: width, height, pixel format, codec name, keyframe timestamp, keyframe index.
The only property VRS itself requires is a codec name for an image content block to be recognized. However, a particular custom codec may require additional properties, such as width, height, and pixel format. Whether a custom codec requires that information or not an implementation detail of each specific custom codec. VRS does not support any particular custom codec by default, they are meant for experimentations and special cases.
image/video
Video image content blocks require additional properties to decode images:
- Required properties: width, height, pixel format, codec name, keyframe timestamp, keyframe index.
- Optional properties: compression quality, which may affect how to decode the image.
Similar to image/raw
images, pixel format and image dimension information must be provided in a single DataLayout
, either in the configuration record or in the data record of the image. Typically, the pixel format and image resolution will not change without a configuration change. Therefore, they are best stored in a configuration record.
On the other hand, the keyframe timestamp and the keyframe index properties change with every frame, and are therefore searched only in a DataLayout
that must be immediately before the image/video
content block.
Reading Images
Image data is received by the RecordFormatStreamPlayer::onImageRead()
callback:
bool onImageRead(
const CurrentRecord& record,
size_t blockIndex,
const ContentBlock& contentBlock);
The RecordFormatStreamPlayer::onDataLayoutRead()
callback happens after the DataLayout
data has been read from disk.
The RecordFormatStreamPlayer::onImageRead()
callback happens before any image data has been read. However, the provided contentBlock
object holds all the image properties, so the following information can be found:
- For all types: the
contentBlock
size is known. That's the size of the buffer you will need to read the image data stored in the image content block. - For
image/raw
images: resolution, strides, and pixel format are defined. - For
image/custom_codec
images: codec name is defined. Resolution, strides, and pixel format might be defined too, if provided in the stream using the Datalayout Conventions. - For
image/video
images: the pixel format, resolution, codec name, keyframe timestamp, and keyframe index are defined.
The image data itself can be read using the RecordReader
object provided by the CurrentRecord
object. The image is described by an ImageContentBlockSpec
object provided by contentBlock.image()
.
image/raw
images
Allocate or reuse a buffer where you can read the image data. Be careful to properly handle the image’s stride and stride2. stride
describes the stride of the first plane (the number of bytes separating the first byte of each successive lines), while stride2
describes the stride of all the following planes, if the images is stored in a multi-plane format.
image/png
, image/jpg
, and image/jxl
images
You will need to read and decode the compressed image data using a standard implementation.
image/custom_codec
images
You will need to read and decode the compressed image data using your own implementation of the custom codec, which implementation is presumably unknown to VRS.
image/video
images
Support for image/video
images is provided by the vrs/xprs module, that depends on ffmpeg, and exposes the codecs built with it.