File frame.hpp
File List > data_format > serialization > frame.hpp
Go to the documentation of this file
#pragma once
#include "illixr/data_format/frame.hpp"
#include "illixr/data_format/serialization/head_pose.hpp"
#include "misc.hpp"
#include
#include
#include
#include
#include
#include
#ifdef ILLIXR_LIBAV
extern "C" {
# include "libavcodec_illixr/avcodec.h"
}
#endif
// ---------------------------------------------------------------------------
// Packet helpers - identical logic to the member functions in frame.hpp but
// as free functions so they can be used from the non-member serializers below.
// ---------------------------------------------------------------------------
namespace ILLIXR::detail {
constexpr std::uint64_t kMaxBobaOverlayFloats =
static_cast<std::uint64_t>(data_format::boba_frame_overlay::max_commands_per_eye) *
data_format::boba_frame_overlay::command_stride_floats;
constexpr std::uint64_t kMaxBobaModalTextureBytes = 16ULL * 1024ULL * 1024ULL;
template<class Archive, typename T>
static void save_sized_vector(Archive& ar, const std::vector<T>& values) {
const std::uint64_t size = values.size();
ar << size;
if (size != 0) {
ar << boost::serialization::make_array(values.data(), values.size());
}
}
template<class Archive, typename T>
static void load_sized_vector(Archive& ar, std::vector<T>& values, std::uint64_t maximum_size, const char* field_name) {
std::uint64_t size = 0;
ar >> size;
if (size > maximum_size || size > static_cast<std::uint64_t>(std::numeric_limits<std::size_t>::max())) {
throw std::runtime_error(std::string{"compressed_frame: invalid "} + field_name + " size");
}
values.resize(static_cast<std::size_t>(size));
if (size != 0) {
ar >> boost::serialization::make_array(values.data(), values.size());
}
}
template<class Archive>
static void save_boba_metadata(Archive& ar, const data_format::boba_frame_overlay& overlay,
const data_format::boba_modal_overlay& modal) {
ar << overlay.source_width;
ar << overlay.source_height;
save_sized_vector(ar, overlay.left_commands);
save_sized_vector(ar, overlay.right_commands);
ar << modal.visible;
ar << modal.left_valid;
ar << modal.right_valid;
ar << modal.texture_id;
ar << modal.width;
ar << modal.height;
for (float value : modal.left_quad_pixels) {
ar << value;
}
for (float value : modal.right_quad_pixels) {
ar << value;
}
ar << modal.width_m;
ar << modal.height_m;
}
template<class Archive>
static void load_boba_metadata(Archive& ar, data_format::boba_frame_overlay& overlay, data_format::boba_modal_overlay& modal) {
ar >> overlay.source_width;
ar >> overlay.source_height;
load_sized_vector(ar, overlay.left_commands, kMaxBobaOverlayFloats, "left Boba overlay");
load_sized_vector(ar, overlay.right_commands, kMaxBobaOverlayFloats, "right Boba overlay");
if (overlay.left_commands.size() % data_format::boba_frame_overlay::command_stride_floats != 0 ||
overlay.right_commands.size() % data_format::boba_frame_overlay::command_stride_floats != 0) {
throw std::runtime_error("compressed_frame: malformed Boba overlay command vector");
}
ar >> modal.visible;
ar >> modal.left_valid;
ar >> modal.right_valid;
ar >> modal.texture_id;
ar >> modal.width;
ar >> modal.height;
for (float& value : modal.left_quad_pixels) {
ar >> value;
}
for (float& value : modal.right_quad_pixels) {
ar >> value;
}
ar >> modal.width_m;
ar >> modal.height_m;
if (modal.width > 8192 || modal.height > 8192 ||
(modal.visible && (modal.texture_id == 0 || modal.width == 0 || modal.height == 0))) {
throw std::runtime_error("compressed_frame: invalid Boba modal metadata");
}
}
#ifdef ILLIXR_LIBAV
template<class Archive>
static void save_packet(Archive& ar, AVPacket* pkt) {
int32_t pkt_size = pkt->size; // Use fixed-width type
ar << pkt_size;
ar << boost::serialization::make_array(pkt->data, pkt->size);
ar << pkt->pts;
ar << pkt->dts;
ar << pkt->stream_index;
ar << pkt->flags;
ar << pkt->duration;
ar << pkt->pos;
ar << pkt->time_base.num;
ar << pkt->time_base.den;
int32_t side_data_elems = pkt->side_data_elems; // Use fixed-width type
ar << side_data_elems;
for (int i = 0; i < pkt->side_data_elems; i++) {
int32_t side_data_type = static_cast<int32_t>(pkt->side_data[i].type);
uint64_t side_data_size = pkt->side_data[i].size;
ar << side_data_type;
ar << side_data_size;
ar << boost::serialization::make_array(pkt->side_data[i].data, pkt->side_data[i].size);
}
}
template<class Archive>
static void load_packet(Archive& ar, AVPacket* pkt) {
int32_t pkt_size; // Use fixed-width type
ar >> pkt_size;
// FFmpeg's bitstream readers require zeroed AV_INPUT_BUFFER_PADDING_SIZE
// bytes beyond the payload. A payload-sized av_buffer_alloc is insufficient.
if (pkt_size < 0 || av_new_packet(pkt, pkt_size) < 0) {
throw std::runtime_error{"Failed to allocate received video packet"};
}
ar >> boost::serialization::make_array(pkt->data, pkt->size);
ar >> pkt->pts;
ar >> pkt->dts;
ar >> pkt->stream_index;
ar >> pkt->flags;
ar >> pkt->duration;
ar >> pkt->pos;
ar >> pkt->time_base.num;
ar >> pkt->time_base.den;
int32_t side_data_elems; // Use fixed-width type
ar >> side_data_elems;
if (side_data_elems < 0) {
throw std::runtime_error{"Invalid video packet side-data count"};
}
for (int i = 0; i < side_data_elems; i++) {
int32_t side_data_type;
uint64_t side_data_size;
ar >> side_data_type;
ar >> side_data_size;
auto* data = av_packet_new_side_data(pkt, static_cast<AVPacketSideDataType>(side_data_type),
static_cast<size_t>(side_data_size));
if (data == nullptr) {
throw std::runtime_error{"Failed to allocate received video packet side data"};
}
ar >> boost::serialization::make_array(data, static_cast<size_t>(side_data_size));
}
}
#elif defined(NVENC_ENCODER) || defined(NVDEC_DECODER)
template<class Archive>
static void save_packet(Archive& ar, const std::vector<uint8_t>& pkt) {
uint64_t vec_size = pkt.size(); // Changed from size_t to uint64_t for cross-platform compatibility
ar << vec_size;
ar << boost::serialization::make_array(pkt.data(), pkt.size());
}
template<class Archive>
static void load_packet(Archive& ar, std::vector<uint8_t>& pkt) {
uint64_t vec_size; // Changed from size_t to uint64_t for cross-platform compatibility
ar >> vec_size;
pkt.resize(static_cast<size_t>(vec_size));
ar >> boost::serialization::make_array(pkt.data(), static_cast<size_t>(vec_size));
}
#endif
} // namespace ILLIXR::detail
// ---------------------------------------------------------------------------
// Non-member serializers in boost::serialization namespace
// ---------------------------------------------------------------------------
namespace boost::serialization {
template<class Archive>
void save(Archive& ar, const ILLIXR::data_format::compressed_frame& f, const unsigned int version) {
(void) version;
ar << boost::serialization::base_object<ILLIXR::switchboard::event>(f);
ar << f.nalu_only;
ar << f.use_depth;
ar << f.use_motion_vectors;
#ifdef ILLIXR_ENABLE_BOBA
const auto presentation_mode = static_cast<std::uint8_t>(f.presentation_mode);
ar << presentation_mode;
ar << f.content_aspect_ratio;
ILLIXR::detail::save_boba_metadata(ar, f.boba_overlay, f.boba_modal);
#endif
#ifdef ILLIXR_LIBAV
if (f.nalu_only) {
int32_t left_size = f.left_color->size; // Use fixed-width type
int32_t right_size = f.right_color->size; // Use fixed-width type
ar << left_size;
ar << right_size;
ar << boost::serialization::make_array(f.left_color->data, f.left_color->size);
ar << boost::serialization::make_array(f.right_color->data, f.right_color->size);
if (f.use_depth) {
int32_t left_depth_size = f.left_depth->size; // Use fixed-width type
int32_t right_depth_size = f.right_depth->size; // Use fixed-width type
ar << left_depth_size;
ar << right_depth_size;
ar << boost::serialization::make_array(f.left_depth->data, f.left_depth->size);
ar << boost::serialization::make_array(f.right_depth->data, f.right_depth->size);
}
if (f.use_motion_vectors) {
int32_t left_mv_size = f.left_motion_vec->size;
int32_t right_mv_size = f.right_motion_vec->size;
ar << left_mv_size;
ar << right_mv_size;
ar << boost::serialization::make_array(f.left_motion_vec->data, f.left_motion_vec->size);
ar << boost::serialization::make_array(f.right_motion_vec->data, f.right_motion_vec->size);
}
} else {
#endif
#if defined(ILLIXR_LIBAV) || defined(NVENC_ENCODER) || defined(NVDEC_DECODER)
ILLIXR::detail::save_packet(ar, f.left_color);
ILLIXR::detail::save_packet(ar, f.right_color);
if (f.use_depth) {
ILLIXR::detail::save_packet(ar, f.left_depth);
ILLIXR::detail::save_packet(ar, f.right_depth);
}
if (f.use_motion_vectors) {
ILLIXR::detail::save_packet(ar, f.left_motion_vec);
ILLIXR::detail::save_packet(ar, f.right_motion_vec);
}
# ifdef ILLIXR_LIBAV
}
# endif
#else
static_assert(false, "Not compiled with libav or NVENC/NVDEC");
#endif
#ifdef USING_OPENXR
ar << f.pose[0];
ar << f.pose[1];
#else
ar << f.pose;
#endif
ar << f.near_z;
ar << f.far_z;
#ifdef USING_OPENXR
ar << f.fov_left[0];
ar << f.fov_left[1];
ar << f.fov_right[0];
ar << f.fov_right[1];
ar << f.fov_up[0];
ar << f.fov_up[1];
ar << f.fov_down[0];
ar << f.fov_down[1];
#endif
ar << f.sent_time;
ar << f.frame_number;
ar << f.pose_id;
ar << f.encode_time;
ar << f.is_keyframe;
ar << f.magic;
}
template<class Archive>
void load(Archive& ar, ILLIXR::data_format::compressed_frame& f, const unsigned int version) {
(void) version;
ar >> boost::serialization::base_object<ILLIXR::switchboard::event>(f);
ar >> f.nalu_only;
ar >> f.use_depth;
ar >> f.use_motion_vectors;
#ifdef ILLIXR_ENABLE_BOBA
std::uint8_t presentation_mode = 0;
ar >> presentation_mode;
if (presentation_mode > static_cast<std::uint8_t>(ILLIXR::data_format::stereo_presentation_mode::head_locked_panel)) {
throw std::runtime_error("compressed_frame: invalid presentation mode");
}
f.presentation_mode = static_cast<ILLIXR::data_format::stereo_presentation_mode>(presentation_mode);
ar >> f.content_aspect_ratio;
ILLIXR::detail::load_boba_metadata(ar, f.boba_overlay, f.boba_modal);
#endif
if (f.nalu_only) {
ar >> f.left_color_nalu_size;
ar >> f.right_color_nalu_size;
f.left_color_nalu = (char*) malloc(f.left_color_nalu_size);
f.right_color_nalu = (char*) malloc(f.right_color_nalu_size);
ar >> boost::serialization::make_array(f.left_color_nalu, f.left_color_nalu_size);
ar >> boost::serialization::make_array(f.right_color_nalu, f.right_color_nalu_size);
if (f.use_depth) {
ar >> f.left_depth_nalu_size;
ar >> f.right_depth_nalu_size;
f.left_depth_nalu = (char*) malloc(f.left_depth_nalu_size);
f.right_depth_nalu = (char*) malloc(f.right_depth_nalu_size);
ar >> boost::serialization::make_array(f.left_depth_nalu, f.left_depth_nalu_size);
ar >> boost::serialization::make_array(f.right_depth_nalu, f.right_depth_nalu_size);
}
} else {
#if defined(ILLIXR_LIBAV) || defined(NVENC_ENCODER) || defined(NVDEC_DECODER)
# ifdef ILLIXR_LIBAV
f.left_color = av_packet_alloc();
f.right_color = av_packet_alloc();
# endif
ILLIXR::detail::load_packet(ar, f.left_color);
ILLIXR::detail::load_packet(ar, f.right_color);
if (f.use_depth) {
# ifdef ILLIXR_LIBAV
f.left_depth = av_packet_alloc();
f.right_depth = av_packet_alloc();
# endif
ILLIXR::detail::load_packet(ar, f.left_depth);
ILLIXR::detail::load_packet(ar, f.right_depth);
}
if (f.use_motion_vectors) {
# ifdef ILLIXR_LIBAV
f.left_motion_vec = av_packet_alloc();
f.right_motion_vec = av_packet_alloc();
# endif
ILLIXR::detail::load_packet(ar, f.left_motion_vec);
ILLIXR::detail::load_packet(ar, f.right_motion_vec);
}
#else
static_assert(false, "Not compiled with libav or NVENC/NVDEC");
#endif
}
#ifdef USING_OPENXR
ar >> f.pose[0];
ar >> f.pose[1];
#else
ar >> f.pose;
#endif
ar >> f.near_z;
ar >> f.far_z;
#ifdef USING_OPENXR
ar >> f.fov_left[0];
ar >> f.fov_left[1];
ar >> f.fov_right[0];
ar >> f.fov_right[1];
ar >> f.fov_up[0];
ar >> f.fov_up[1];
ar >> f.fov_down[0];
ar >> f.fov_down[1];
#endif
ar >> f.sent_time;
ar >> f.frame_number;
ar >> f.pose_id;
ar >> f.encode_time;
ar >> f.is_keyframe;
ar >> f.magic;
if (f.magic != 0xdeadbeef) {
throw std::runtime_error("compressed_frame: magic number mismatch");
}
}
// Register the split free functions with Boost
template<class Archive>
void serialize(Archive& ar, ILLIXR::data_format::compressed_frame& f, const unsigned int version) {
boost::serialization::split_free(ar, f, version);
}
template<class Archive>
void save(Archive& ar, const ILLIXR::data_format::boba_modal_texture& texture, const unsigned int version) {
(void) version;
ar << boost::serialization::base_object<ILLIXR::switchboard::event>(texture);
ar << texture.texture_id;
ar << texture.width;
ar << texture.height;
ILLIXR::detail::save_sized_vector(ar, texture.rgba);
ar << texture.magic;
}
template<class Archive>
void load(Archive& ar, ILLIXR::data_format::boba_modal_texture& texture, const unsigned int version) {
(void) version;
ar >> boost::serialization::base_object<ILLIXR::switchboard::event>(texture);
ar >> texture.texture_id;
ar >> texture.width;
ar >> texture.height;
ILLIXR::detail::load_sized_vector(ar, texture.rgba, ILLIXR::detail::kMaxBobaModalTextureBytes, "Boba modal texture");
ar >> texture.magic;
const std::uint64_t required_bytes = static_cast<std::uint64_t>(texture.width) * texture.height * 4ULL;
if (texture.magic != ILLIXR::data_format::boba_modal_texture::wire_magic || texture.texture_id == 0 || texture.width == 0 ||
texture.height == 0 || texture.width > 8192 || texture.height > 8192 || required_bytes != texture.rgba.size()) {
throw std::runtime_error("boba_modal_texture: invalid payload");
}
}
template<class Archive>
void serialize(Archive& ar, ILLIXR::data_format::boba_modal_texture& texture, const unsigned int version) {
boost::serialization::split_free(ar, texture, version);
}
} // namespace boost::serialization
BOOST_CLASS_EXPORT_KEY(ILLIXR::data_format::compressed_frame)
BOOST_CLASS_EXPORT_KEY(ILLIXR::data_format::boba_modal_texture)