Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 74 additions & 1 deletion include/av2_obu/obus/atlas_segment_obu.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,91 @@

#include <av2_obu/core/base_obu.h>

#include <vector>

namespace av2_obu {

// Atlas Segment OBU
class AtlasSegmentOBU : public BaseOBU {
public:
explicit AtlasSegmentOBU(const OBUPosition& pos) : BaseOBU(pos) {}

json to_json() const override;
std::string type_name() const override { return "ATLAS_SEGMENT"; }

enum class ModeIdc : uint32_t {
ENHANCED_ATLAS = 0,
BASIC_ATLAS = 1,
SINGLE_ATLAS = 2,
MULTISTREAM_ATLAS = 3,
MULTISTREAM_ALPHA_ATLAS = 4,
};

struct Segment {
uint32_t input_stream_id = 0;
uint32_t top_left_x = 0;
uint32_t top_left_y = 0;
uint32_t width = 0;
uint32_t height = 0;
uint32_t alpha_segment_flag = 0;
};

struct EnhancedAtlasInfo {
uint32_t num_region_columns_minus1 = 0;
uint32_t num_region_rows_minus1 = 0;
uint32_t uniform_spacing_flag = 0;
std::vector<uint32_t> column_width_minus1;
std::vector<uint32_t> row_height_minus1;
uint32_t region_width_minus1 = 0;
uint32_t region_height_minus1 = 0;
uint32_t single_region_per_segment_flag = 0;
uint32_t num_atlas_segments_minus1 = 0;
struct SegmentRegion {
uint32_t top_left_col = 0;
uint32_t top_left_row = 0;
uint32_t bottom_right_col_off = 0;
uint32_t bottom_right_row_off = 0;
};
std::vector<SegmentRegion> segment_regions;
};

struct LabelSegmentInfo {
uint32_t signaled_ids_flag = 0;
std::vector<uint32_t> segment_ids;
};

protected:
bool parse_payload(std::ifstream& ifs) override;

private:
uint32_t atlas_segment_id_ = 0;
ModeIdc mode_idc_ = ModeIdc::SINGLE_ATLAS;

// Mode 0 — ENHANCED_ATLAS
EnhancedAtlasInfo enhanced_;

// Mode 1 — BASIC_ATLAS
uint32_t basic_stream_id_present_ = 0;
uint32_t basic_width_ = 0;
uint32_t basic_height_ = 0;

// Mode 2 — SINGLE_ATLAS
uint32_t nominal_width_minus1_ = 0;
uint32_t nominal_height_minus1_ = 0;

// Modes 3 & 4 — MULTISTREAM_ATLAS / MULTISTREAM_ALPHA_ATLAS
uint32_t msi_width_ = 0;
uint32_t msi_height_ = 0;
uint32_t msi_background_present_ = 0;
uint32_t msi_bg_r_ = 0;
uint32_t msi_bg_g_ = 0;
uint32_t msi_bg_b_ = 0;
uint32_t msi_alpha_segments_present_ = 0;

// All modes — per-segment data
std::vector<Segment> segments_;

// All modes — label info
LabelSegmentInfo label_;
};

} // namespace av2_obu
253 changes: 249 additions & 4 deletions src/obus/atlas_segment_obu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,274 @@

#include <spdlog/spdlog.h>

#include <av2_obu/core/bitstream_reader.h>
#include <av2_obu/obus/atlas_segment_obu.h>

namespace av2_obu {

namespace {

static const char* mode_name(AtlasSegmentOBU::ModeIdc m) {
switch (m) {
case AtlasSegmentOBU::ModeIdc::ENHANCED_ATLAS: return "ENHANCED_ATLAS";
case AtlasSegmentOBU::ModeIdc::BASIC_ATLAS: return "BASIC_ATLAS";
case AtlasSegmentOBU::ModeIdc::SINGLE_ATLAS: return "SINGLE_ATLAS";
case AtlasSegmentOBU::ModeIdc::MULTISTREAM_ATLAS: return "MULTISTREAM_ATLAS";
case AtlasSegmentOBU::ModeIdc::MULTISTREAM_ALPHA_ATLAS: return "MULTISTREAM_ALPHA_ATLAS";
default: return "UNKNOWN";
}
}

// ats_region_info() — returns NumRegionsInAtlas
static uint32_t parse_region_info(BitstreamReader& br,
AtlasSegmentOBU::EnhancedAtlasInfo& info) {
info.num_region_columns_minus1 = br.read_uvlc();
info.num_region_rows_minus1 = br.read_uvlc();
info.uniform_spacing_flag = br.read_bit();

if (!info.uniform_spacing_flag) {
uint32_t num_cols = info.num_region_columns_minus1 + 1;
uint32_t num_rows = info.num_region_rows_minus1 + 1;
info.column_width_minus1.resize(num_cols);
info.row_height_minus1.resize(num_rows);
for (uint32_t i = 0; i < num_cols; i++)
info.column_width_minus1[i] = br.read_uvlc();
for (uint32_t i = 0; i < num_rows; i++)
info.row_height_minus1[i] = br.read_uvlc();
} else {
info.region_width_minus1 = br.read_uvlc();
info.region_height_minus1 = br.read_uvlc();
}
return (info.num_region_columns_minus1 + 1) * (info.num_region_rows_minus1 + 1);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MAX_ATLAS_COLS = MAX_ATLAS_ROWS = 64

So for a conformant stream, (cols+1) * (rows+1) maxes at 4096. Not even close to uint16 overflow, let alone uint32.

}

// ats_region_to_segment_mapping() — returns numSegments
static uint32_t parse_region_to_segment_mapping(BitstreamReader& br,
AtlasSegmentOBU::EnhancedAtlasInfo& info,
uint32_t num_regions) {
info.single_region_per_segment_flag = br.read_bit();
if (!info.single_region_per_segment_flag) {
info.num_atlas_segments_minus1 = br.read_uvlc();
uint32_t n = info.num_atlas_segments_minus1 + 1;
info.segment_regions.resize(n);
for (uint32_t i = 0; i < n; i++) {
info.segment_regions[i].top_left_col = br.read_uvlc();
info.segment_regions[i].top_left_row = br.read_uvlc();
info.segment_regions[i].bottom_right_col_off = br.read_uvlc();
info.segment_regions[i].bottom_right_row_off = br.read_uvlc();
}
return n;
} else {
info.num_atlas_segments_minus1 = num_regions - 1;
return num_regions;
}
}

// ats_label_segment_info()
static void parse_label_segment_info(BitstreamReader& br, AtlasSegmentOBU::LabelSegmentInfo& label,
uint32_t num_segments) {
label.signaled_ids_flag = br.read_bit();
if (label.signaled_ids_flag) {
label.segment_ids.resize(num_segments);
for (uint32_t i = 0; i < num_segments; i++)
label.segment_ids[i] = br.read_bits(8);
}
}

} // namespace

bool AtlasSegmentOBU::parse_payload(std::ifstream& ifs) {
spdlog::debug("Parsing ATLAS_SEGMENT payload ({} bytes)", position_.payload_size);

// Read raw payload
raw_payload_.resize(position_.payload_size);
if (!ifs.read(reinterpret_cast<char*>(raw_payload_.data()), position_.payload_size)) {
spdlog::error("Failed to read ATLAS_SEGMENT payload");
return false;
}

// TODO: Implement ATLAS_SEGMENT parsing
spdlog::warn("ATLAS_SEGMENT parsing not yet implemented");
try {
BitstreamReader br(raw_payload_);

atlas_segment_id_ = br.read_bits(3);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

atlas_segment_id in atlas_segment_info_obu( ) is a 3-bit field, unlike lcr_layer_atlas_segment_id, which is 8-bit.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

funny how AI might lead one into wrong direction :)

mode_idc_ = static_cast<ModeIdc>(br.read_uvlc());

uint32_t num_segments = 0;

switch (mode_idc_) {
case ModeIdc::ENHANCED_ATLAS: {
uint32_t num_regions = parse_region_info(br, enhanced_);
num_segments = parse_region_to_segment_mapping(br, enhanced_, num_regions);
break;
}
case ModeIdc::BASIC_ATLAS: {
basic_stream_id_present_ = br.read_bit();
basic_width_ = br.read_uvlc();
basic_height_ = br.read_uvlc();
uint32_t n = br.read_uvlc() + 1; // ats_num_atlas_segments_minus1 + 1
num_segments = n;
segments_.resize(n);
for (uint32_t i = 0; i < n; i++) {
if (basic_stream_id_present_)
segments_[i].input_stream_id = br.read_bits(5);
segments_[i].top_left_x = br.read_uvlc();
segments_[i].top_left_y = br.read_uvlc();
segments_[i].width = br.read_uvlc();
segments_[i].height = br.read_uvlc();
}
break;
}
case ModeIdc::SINGLE_ATLAS: {
nominal_width_minus1_ = br.read_uvlc();
nominal_height_minus1_ = br.read_uvlc();
num_segments = 1;
break;
}
case ModeIdc::MULTISTREAM_ATLAS:
case ModeIdc::MULTISTREAM_ALPHA_ATLAS: {
bool with_alpha = (mode_idc_ == ModeIdc::MULTISTREAM_ALPHA_ATLAS);
msi_width_ = br.read_uvlc();
msi_height_ = br.read_uvlc();
uint32_t n = br.read_uvlc() + 1; // ats_msi_num_atlas_segments_minus1 + 1
num_segments = n;
if (with_alpha)
msi_alpha_segments_present_ = br.read_bit();
msi_background_present_ = br.read_bit();
if (msi_background_present_) {
msi_bg_r_ = br.read_bits(8);
msi_bg_g_ = br.read_bits(8);
msi_bg_b_ = br.read_bits(8);
}
segments_.resize(n);
for (uint32_t i = 0; i < n; i++) {
segments_[i].input_stream_id = br.read_bits(5);
segments_[i].top_left_x = br.read_uvlc();
segments_[i].top_left_y = br.read_uvlc();
segments_[i].width = br.read_uvlc();
segments_[i].height = br.read_uvlc();
if (with_alpha && msi_alpha_segments_present_ && i != n - 1)
segments_[i].alpha_segment_flag = br.read_bit();
}
break;
}
default:
spdlog::warn("ATLAS_SEGMENT: unknown mode_idc={}", static_cast<uint32_t>(mode_idc_));
return false;
}

parse_label_segment_info(br, label_, num_segments);

if (!parse_obu_trailing_bits(br))
return false;

} catch (const std::runtime_error& e) {
spdlog::error("ATLAS_SEGMENT parse error: {}", e.what());
return false;
}

spdlog::debug("ATLAS_SEGMENT: id={}, mode={}", atlas_segment_id_, mode_name(mode_idc_));
return true;
}

json AtlasSegmentOBU::to_json() const {
json j = BaseOBU::to_json();
j["type_name"] = "ATLAS_SEGMENT";
// TODO: Add ATLAS_SEGMENT-specific fields

if (!parsed_) return j;

j["atlas_segment_id"] = atlas_segment_id_;
j["ats_atlas_segment_mode_idc"] = static_cast<uint32_t>(mode_idc_);
j["ats_atlas_segment_mode_name"] = mode_name(mode_idc_);

switch (mode_idc_) {
case ModeIdc::ENHANCED_ATLAS: {
json e;
e["ats_num_region_columns_minus_1"] = enhanced_.num_region_columns_minus1;
e["ats_num_region_rows_minus_1"] = enhanced_.num_region_rows_minus1;
e["ats_uniform_spacing_flag"] = enhanced_.uniform_spacing_flag;
if (!enhanced_.uniform_spacing_flag) {
e["ats_column_width_minus_1"] = enhanced_.column_width_minus1;
e["ats_row_height_minus_1"] = enhanced_.row_height_minus1;
} else {
e["ats_region_width_minus_1"] = enhanced_.region_width_minus1;
e["ats_region_height_minus_1"] = enhanced_.region_height_minus1;
}
e["ats_single_region_per_atlas_segment_flag"] = enhanced_.single_region_per_segment_flag;
e["ats_num_atlas_segments_minus_1"] = enhanced_.num_atlas_segments_minus1;
if (!enhanced_.single_region_per_segment_flag) {
json regions = json::array();
for (const auto& r : enhanced_.segment_regions) {
regions.push_back({{"ats_top_left_region_column", r.top_left_col},
{"ats_top_left_region_row", r.top_left_row},
{"ats_bottom_right_region_column_off", r.bottom_right_col_off},
{"ats_bottom_right_region_row_off", r.bottom_right_row_off}});
}
e["segment_regions"] = regions;
}
j["enhanced_atlas_info"] = e;
break;
}
case ModeIdc::BASIC_ATLAS: {
j["ats_stream_id_present"] = basic_stream_id_present_;
j["ats_width"] = basic_width_;
j["ats_height"] = basic_height_;
j["ats_num_atlas_segments_minus_1"] = segments_.empty() ? 0 : (uint32_t)segments_.size() - 1;
json segs = json::array();
for (const auto& s : segments_) {
json seg;
if (basic_stream_id_present_)
seg["ats_input_stream_id"] = s.input_stream_id;
seg["ats_segment_top_left_pos_x"] = s.top_left_x;
seg["ats_segment_top_left_pos_y"] = s.top_left_y;
seg["ats_segment_width"] = s.width;
seg["ats_segment_height"] = s.height;
segs.push_back(seg);
}
j["segments"] = segs;
break;
}
case ModeIdc::SINGLE_ATLAS:
j["ats_nominal_width_minus_1"] = nominal_width_minus1_;
j["ats_nominal_height_minus_1"] = nominal_height_minus1_;
break;
case ModeIdc::MULTISTREAM_ATLAS:
case ModeIdc::MULTISTREAM_ALPHA_ATLAS: {
j["ats_msi_width"] = msi_width_;
j["ats_msi_height"] = msi_height_;
j["ats_msi_num_atlas_segments_minus_1"] =
segments_.empty() ? 0 : (uint32_t)segments_.size() - 1;
if (mode_idc_ == ModeIdc::MULTISTREAM_ALPHA_ATLAS)
j["ats_msi_alpha_segments_present_flag"] = msi_alpha_segments_present_;
j["ats_msi_background_info_present_flag"] = msi_background_present_;
if (msi_background_present_) {
j["ats_msi_background_red_value"] = msi_bg_r_;
j["ats_msi_background_green_value"] = msi_bg_g_;
j["ats_msi_background_blue_value"] = msi_bg_b_;
}
json segs = json::array();
for (const auto& s : segments_) {
json seg;
seg["ats_msi_input_stream_id"] = s.input_stream_id;
seg["ats_msi_segment_top_left_pos_x"] = s.top_left_x;
seg["ats_msi_segment_top_left_pos_y"] = s.top_left_y;
seg["ats_msi_segment_width"] = s.width;
seg["ats_msi_segment_height"] = s.height;
if (mode_idc_ == ModeIdc::MULTISTREAM_ALPHA_ATLAS)
seg["ats_msi_alpha_segment_flag"] = s.alpha_segment_flag;
segs.push_back(seg);
}
j["segments"] = segs;
break;
}
default:
break;
}

json label;
label["ats_signaled_atlas_segment_ids_flag"] = label_.signaled_ids_flag;
if (label_.signaled_ids_flag)
label["ats_atlas_segment_ids"] = label_.segment_ids;
j["label_segment_info"] = label;

return j;
}

Expand Down