Skip to content
Open
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
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ build/avmdec --all-layers --num-streams=2 -o decoded_%d.y4m stereo_muxed.obu

## Documentation

- **[Multi-XLayer Encoding Guide](doc/multi_xlayer_encoding.md)**:
- **[Multi-XLayer Encoding Guide](https://github.com/AOMediaCodec/avm/blob/multi-layer-av2/doc/multi_xlayer_encoding.md)**:
full reference covering JSON schema, embedded layers, atlas, OPS,
GOP modes, use cases, and constraints.

Expand Down
62 changes: 41 additions & 21 deletions apps/avmdec.c
Original file line number Diff line number Diff line change
Expand Up @@ -706,15 +706,16 @@ static FILE *open_outfile(const char *name) {
}

// Dynamic composite groups derived from LCR layer properties.
// Each unique (layer_type, auxiliary_type, view_type) combination
// Each unique (layer_type, auxiliary_type, LcrViewId) combination
// produces a separate composite output. Layers within a group must
// share the same chroma format; mixed chroma forces separate outputs.
// Mixed bit depth is handled by promoting to the highest bit depth.
typedef struct CompositeGroup {
int layer_type; // TEXTURE_LAYER, AUX_LAYER, etc.
int auxiliary_type; // only meaningful when layer_type == AUX_LAYER
int view_type; // VIEW_UNSPECIFIED, VIEW_CENTER, VIEW_LEFT, etc.
int num_xlayers; // how many xlayers belong to this group
int layer_type; // TEXTURE_LAYER, AUX_LAYER, etc.
int auxiliary_type; // only meaningful when layer_type == AUX_LAYER
int derived_view_id; // LcrViewId: 0-3 = VIEW_UNSPECIFIED/CENTER/LEFT/
// RIGHT, 256 + lcr_view_id for VIEW_EXPLICIT
int num_xlayers; // how many xlayers belong to this group
int xlayer_ids[MAX_NUM_XLAYERS]; // xlayer_ids in this group
int xlayer_indices[MAX_NUM_XLAYERS]; // indices into xlayer_cfg.xlayers[]
// (-1 if from decoder query)
Expand All @@ -736,12 +737,13 @@ static const char *comp_view_type_names[] = { "unspecified", "center", "left",

// Build composite groups from arrays of per-xlayer properties.
// Allocates comp_groups and fills *out_groups / *out_num_groups.
// xlayer_ids[], layer_types[], aux_types[], view_types[] are parallel arrays
// xlayer_ids[], layer_types[], aux_types[], derived_view_ids[] are parallel
// arrays (derived_view_ids[] holds LcrViewId values)
// of length num_xlayers. config_indices[] provides the JSON config index for
// each xlayer (-1 if built from decoder query).
static void build_composite_groups(int num_xlayers, const int *xlayer_ids,
const int *layer_types, const int *aux_types,
const int *view_types,
const int *derived_view_ids,
const int *config_indices,
CompositeGroup **out_groups,
int *out_num_groups) {
Expand All @@ -752,12 +754,12 @@ static void build_composite_groups(int num_xlayers, const int *xlayer_ids,
for (int i = 0; i < num_xlayers; i++) {
int lt = layer_types[i];
int at = aux_types[i];
int vt = view_types[i];
int derived_vid = derived_view_ids[i];
// Find existing group or create new
int gidx = -1;
for (int g = 0; g < num_groups; g++) {
if (groups[g].layer_type == lt && groups[g].auxiliary_type == at &&
groups[g].view_type == vt) {
groups[g].derived_view_id == derived_vid) {
gidx = g;
break;
}
Expand All @@ -766,7 +768,7 @@ static void build_composite_groups(int num_xlayers, const int *xlayer_ids,
gidx = num_groups++;
groups[gidx].layer_type = lt;
groups[gidx].auxiliary_type = at;
groups[gidx].view_type = vt;
groups[gidx].derived_view_id = derived_vid;
groups[gidx].num_xlayers = 0;
groups[gidx].canvas = NULL;
groups[gidx].outfile_cg = NULL;
Expand All @@ -785,9 +787,19 @@ static void build_composite_groups(int num_xlayers, const int *xlayer_ids,
const char *lt_name = (cg->layer_type >= 0 && cg->layer_type < 4)
? comp_layer_type_names[cg->layer_type]
: "unknown";
const char *vt_name = (cg->view_type >= 0 && cg->view_type < 5)
? comp_view_type_names[cg->view_type]
: "unknown";
// Non-explicit views are named by view type; explicit views by their
// lcr_view_id (LcrViewId - 256) so that different views get different
// labels (and therefore different output files).
char vt_name[32];
if (cg->derived_view_id >= LCR_VIEW_ID_EXPLICIT_BASE) {
snprintf(vt_name, sizeof(vt_name), "view%d",
cg->derived_view_id - LCR_VIEW_ID_EXPLICIT_BASE);
} else if (cg->derived_view_id >= 0 && cg->derived_view_id < 5) {
snprintf(vt_name, sizeof(vt_name), "%s",
comp_view_type_names[cg->derived_view_id]);
} else {
snprintf(vt_name, sizeof(vt_name), "unknown");
}
if (cg->layer_type == AUX_LAYER && cg->auxiliary_type >= 0 &&
cg->auxiliary_type < 4) {
snprintf(cg->label, sizeof(cg->label), "%s_%s_%s",
Expand Down Expand Up @@ -1085,18 +1097,25 @@ static int main_loop(int argc, const char **argv_) {
// Build composite groups eagerly from JSON config
if (atlas_composite && xlayer_cfg.enable_atlas) {
int xlids[MAX_NUM_XLAYERS], lts[MAX_NUM_XLAYERS];
int ats[MAX_NUM_XLAYERS], vts[MAX_NUM_XLAYERS];
int ats[MAX_NUM_XLAYERS], derived_vids[MAX_NUM_XLAYERS];
int idxs[MAX_NUM_XLAYERS];
for (int xi = 0; xi < xlayer_cfg.num_xlayers; xi++) {
const XLayerEncConfig *xl = &xlayer_cfg.xlayers[xi];
xlids[xi] = xl->xlayer_id;
lts[xi] = xl->layer_type;
ats[xi] = (xl->layer_type == AUX_LAYER) ? xl->auxiliary_type : -1;
vts[xi] = xl->view_type;
const int view_id = xlayer_config_lcr_view_id(xl);
if (xl->view_type == VIEW_EXPLICIT && view_id < 0) {
die("Error: xlayer %d has view_type \"explicit\" but no valid "
"view_id (0-255)\n",
xl->xlayer_id);
}
derived_vids[xi] = av2_lcr_view_id(xl->view_type, view_id);
idxs[xi] = xi;
}
build_composite_groups(xlayer_cfg.num_xlayers, xlids, lts, ats, vts, idxs,
&comp_groups, &num_comp_groups);
build_composite_groups(xlayer_cfg.num_xlayers, xlids, lts, ats,
derived_vids, idxs, &comp_groups,
&num_comp_groups);
comp_groups_built = 1;
}
}
Expand Down Expand Up @@ -1384,15 +1403,16 @@ static int main_loop(int argc, const char **argv_) {
&decoder, AV2D_GET_ATLAS_INFO, &dec_atlas_info);

if (have_lcr && lcr_info.num_xlayers > 0) {
int xlids[31], lts[31], ats_arr[31], vts[31];
int xlids[31], lts[31], ats_arr[31], derived_vids[31];
for (int li = 0; li < lcr_info.num_xlayers; li++) {
xlids[li] = lcr_info.xlayers[li].xlayer_id;
lts[li] = lcr_info.xlayers[li].layer_type;
ats_arr[li] = lcr_info.xlayers[li].auxiliary_type;
vts[li] = lcr_info.xlayers[li].view_type;
derived_vids[li] = lcr_info.xlayers[li].derived_view_id;
}
build_composite_groups(lcr_info.num_xlayers, xlids, lts, ats_arr, vts,
NULL, &comp_groups, &num_comp_groups);
build_composite_groups(lcr_info.num_xlayers, xlids, lts, ats_arr,
derived_vids, NULL, &comp_groups,
&num_comp_groups);
comp_groups_built = 1;

if (have_atlas && dec_atlas_info.num_segments > 0) {
Expand Down
4 changes: 4 additions & 0 deletions av2/av2_dx_iface.c
Original file line number Diff line number Diff line change
Expand Up @@ -2049,11 +2049,13 @@ static avm_codec_err_t ctrl_get_lcr_info(avm_codec_alg_priv_t *ctx,
xl->auxiliary_type =
(ml->lcr_layer_type[0] == 1) ? ml->lcr_auxiliary_type[0] : -1;
xl->view_type = ml->lcr_view_type[0];
xl->derived_view_id = ml->LcrViewId[0];
} else {
xl->num_mlayers = 0;
xl->layer_type = 0;
xl->auxiliary_type = -1;
xl->view_type = 0;
xl->derived_view_id = 0;
}
}
return AVM_CODEC_OK;
Expand All @@ -2078,11 +2080,13 @@ static avm_codec_err_t ctrl_get_lcr_info(avm_codec_alg_priv_t *ctx,
xl->auxiliary_type =
(ml->lcr_layer_type[0] == 1) ? ml->lcr_auxiliary_type[0] : -1;
xl->view_type = ml->lcr_view_type[0];
xl->derived_view_id = ml->LcrViewId[0];
} else {
xl->num_mlayers = 0;
xl->layer_type = 0;
xl->auxiliary_type = -1;
xl->view_type = 0;
xl->derived_view_id = 0;
}
count++;
break; // found LCR for this xlayer, move to next
Expand Down
2 changes: 2 additions & 0 deletions av2/common/av2_common_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,8 @@ typedef struct EmbeddedLayerInfo {
int lcr_auxiliary_type[MAX_NUM_MLAYERS];
int lcr_view_type[MAX_NUM_MLAYERS];
int lcr_view_id[MAX_NUM_MLAYERS];
// Derived: av2_lcr_view_id(lcr_view_type, lcr_view_id)
int LcrViewId[MAX_NUM_MLAYERS];
int lcr_dependent_layer_map[MAX_NUM_MLAYERS];
int lcr_same_sh_max_resolution_flag[MAX_NUM_MLAYERS];
int lcr_max_expected_width[MAX_NUM_MLAYERS];
Expand Down
14 changes: 13 additions & 1 deletion av2/common/enums.h
Original file line number Diff line number Diff line change
Expand Up @@ -355,10 +355,22 @@ enum {
VIEW_LEFT = 2,
VIEW_RIGHT = 3,
VIEW_EXPLICIT = 4,
// Reserved: 5-7
// Reserved: 5-255
VIEW_TYPES = 8
};

// LcrViewId identifies the view of an embedded layer with a single value:
// 256 + lcr_view_id when lcr_view_type is VIEW_EXPLICIT (256..511), and
// lcr_view_type otherwise (0..255). lcr_view_id is only signalled for
// VIEW_EXPLICIT, so its value is not used for the other view types.
#define LCR_VIEW_ID_EXPLICIT_BASE 256

static AVM_INLINE int av2_lcr_view_id(int lcr_view_type, int lcr_view_id) {
return lcr_view_type == VIEW_EXPLICIT
? LCR_VIEW_ID_EXPLICIT_BASE + lcr_view_id
: lcr_view_type;
}

enum {
ENHANCED_ATLAS = 0,
BASIC_ATLAS = 1,
Expand Down
3 changes: 2 additions & 1 deletion av2/decoder/obu.c
Original file line number Diff line number Diff line change
Expand Up @@ -1766,7 +1766,8 @@ int check_temporal_unit_structure(temporal_unit_state_t *state, int obu_type,

// After the global phase, an xlayer_id change starts a fresh per-xlayer
// group. No increasing-xlayer constraint: a TU may group all per-xlayer
// config ahead of all frame data (config carried in the ISOBMFF sample entry).
// config ahead of all frame data (config carried in the ISOBMFF sample
// entry).
if (*state == TU_STATE_LOCAL_INFO || *state == TU_STATE_SEQUENCE_HEADER ||
*state == TU_STATE_FRAME_UINT_DATA) {
if (xlayer_id != GLOBAL_XLAYER_ID && prev_xlayer_id != GLOBAL_XLAYER_ID &&
Expand Down
2 changes: 2 additions & 0 deletions av2/decoder/obu_lcr.c
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ static int read_lcr_embedded_layer_info(struct LCRXLayerInfo *xlayer_info,
if (mlayer_params->lcr_view_type[i] == VIEW_EXPLICIT) {
mlayer_params->lcr_view_id[i] = avm_rb_read_literal(rb, 8);
}
mlayer_params->LcrViewId[i] = av2_lcr_view_id(
mlayer_params->lcr_view_type[i], mlayer_params->lcr_view_id[i]);
if (i > 0) {
mlayer_params->lcr_dependent_layer_map[i] = avm_rb_read_literal(rb, i);
}
Expand Down
22 changes: 13 additions & 9 deletions avm/avmdx.h
Original file line number Diff line number Diff line change
Expand Up @@ -168,15 +168,19 @@ typedef struct avm_screen_content_tools_info {
* extended layer. Populated from Global or Local LCR OBU data.
*/
typedef struct avm_xlayer_layer_info {
int xlayer_id; /**< Extended layer ID (0-30) */
int layer_type; /**< 0=texture, 1=auxiliary, 2=stereo, 3=dependent */
int auxiliary_type; /**< 0=alpha, 1=depth, 2=segmentation, 3=gain_map
(-1 if N/A) */
int view_type; /**< 0=unspecified, 1=center, 2=left, 3=right,
4=explicit */
int max_width; /**< lcr_max_pic_width from RepresentationInfo */
int max_height; /**< lcr_max_pic_height from RepresentationInfo */
int num_mlayers; /**< Number of embedded (media) layers */
int xlayer_id; /**< Extended layer ID (0-30) */
int layer_type; /**< 0=texture, 1=auxiliary, 2=stereo, 3=dependent */
int auxiliary_type; /**< 0=alpha, 1=depth, 2=segmentation, 3=gain_map
(-1 if N/A) */
int view_type; /**< 0=unspecified, 1=center, 2=left, 3=right,
4=explicit */
int max_width; /**< lcr_max_pic_width from RepresentationInfo */
int max_height; /**< lcr_max_pic_height from RepresentationInfo */
int num_mlayers; /**< Number of embedded (media) layers */
int derived_view_id; /**< LcrViewId of embedded layer 0, derived from
lcr_view_type and lcr_view_id: view_type for
non-explicit views (0-3), 256 + lcr_view_id for
explicit views (256-511) */
} avm_xlayer_layer_info_t;

/*!\brief LCR info exposed by AV2D_GET_LCR_INFO.
Expand Down
11 changes: 11 additions & 0 deletions common/tu_assembler.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include "common/tu_assembler.h"

#include <assert.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
Expand Down Expand Up @@ -1268,6 +1269,11 @@ void populate_global_lcr_from_config(const MultiXLayerConfig *mcfg,
ml->lcr_auxiliary_type[m] = xl->auxiliary_type;
}
ml->lcr_view_type[m] = xl->view_type;
const int view_id = xlayer_config_lcr_view_id(xl);
// VIEW_EXPLICIT needs a valid view_id (checked by config validation).
assert(xl->view_type != VIEW_EXPLICIT || view_id >= 0);
ml->lcr_view_id[m] = view_id;
ml->LcrViewId[m] = av2_lcr_view_id(xl->view_type, view_id);
ml->TLayerCount[m] = xl->num_temporal_layers;
ml->lcr_tlayer_map[m] = (1 << xl->num_temporal_layers) - 1;
// Set resolution flag based on scaling mode.
Expand Down Expand Up @@ -1300,6 +1306,11 @@ void populate_global_lcr_from_config(const MultiXLayerConfig *mcfg,
ml->lcr_auxiliary_type[0] = xl->auxiliary_type;
}
ml->lcr_view_type[0] = xl->view_type;
const int view_id = xlayer_config_lcr_view_id(xl);
// VIEW_EXPLICIT needs a valid view_id (checked by config validation).
assert(xl->view_type != VIEW_EXPLICIT || view_id >= 0);
ml->lcr_view_id[0] = view_id;
ml->LcrViewId[0] = av2_lcr_view_id(xl->view_type, view_id);
ml->TLayerCount[0] = xl->num_temporal_layers;
ml->lcr_tlayer_map[0] = (1 << xl->num_temporal_layers) - 1;
ml->lcr_same_sh_max_resolution_flag[0] = 1;
Expand Down
10 changes: 10 additions & 0 deletions common/xlayer_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ typedef struct XLayerEncConfig {
int layer_type; // TEXTURE_LAYER, AUX_LAYER, STEREO_LAYER, etc.
int auxiliary_type; // LCR_ALPHA_AUX, LCR_DEPTH_AUX, etc. (if AUX_LAYER)
int view_type; // VIEW_UNSPECIFIED, VIEW_LEFT, VIEW_RIGHT, etc.
int view_id; // lcr_view_id (0-255), -1 if not set. Required for
// VIEW_EXPLICIT and not signalled otherwise.
int num_temporal_layers;
int num_embedded_layers;
// Color info
Expand Down Expand Up @@ -127,6 +129,13 @@ typedef struct XLayerEncConfig {
} codec_controls[MAX_CODEC_CONTROLS];
} XLayerEncConfig;

// lcr_view_id is signalled only when lcr_view_type is VIEW_EXPLICIT.
// When it is signalled, it must be 0-255.
static inline int xlayer_config_lcr_view_id(const XLayerEncConfig *xl) {
if (xl->view_type != VIEW_EXPLICIT) return -1;
return (xl->view_id >= 0 && xl->view_id <= 255) ? xl->view_id : -1;
}

// Per operating-point configuration within an OPS set
typedef struct OperatingPointConfig {
int intent; // OPS intent (display, monitoring, etc.)
Expand Down Expand Up @@ -224,6 +233,7 @@ static inline void xlayer_config_init(MultiXLayerConfig *cfg) {
cfg->xlayers[i].num_temporal_layers = 1;
cfg->xlayers[i].num_embedded_layers = 1;
cfg->xlayers[i].view_type = VIEW_UNSPECIFIED;
cfg->xlayers[i].view_id = -1;
for (int j = 0; j < MAX_NUM_MLAYERS; j++) {
cfg->xlayers[i].mlayer_sources[j].input_source_idx = -1;
cfg->xlayers[i].mlayer_sources[j].atlas_pos_x = -1;
Expand Down
27 changes: 27 additions & 0 deletions common/xlayer_config_parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ static int parse_xlayer_entry(const cJSON *entry, XLayerEncConfig *xlcfg) {

const char *vt = json_get_string(entry, "view_type", NULL);
if (vt) xlcfg->view_type = parse_view_type(vt);
// lcr_view_id is signalled only when lcr_view_type is VIEW_EXPLICIT.
xlcfg->view_id = json_get_int(entry, "view_id", -1);

xlcfg->num_temporal_layers = json_get_int(entry, "num_temporal_layers", 1);
xlcfg->num_embedded_layers = json_get_int(entry, "num_embedded_layers", 1);
Expand Down Expand Up @@ -483,6 +485,7 @@ static int parse_xlayer_entry(const cJSON *entry, XLayerEncConfig *xlcfg) {
"layer_type",
"auxiliary_type",
"view_type",
"view_id",
"num_temporal_layers",
"num_embedded_layers",
"color_primaries",
Expand Down Expand Up @@ -990,6 +993,30 @@ int validate_multi_xlayer_config(const MultiXLayerConfig *cfg) {
seen[id] = 1;
}

// lcr_view_id is an 8-bit field that is only signalled for VIEW_EXPLICIT,
// where it is required. view_id is -1 when it is not set.
for (int i = 0; i < cfg->num_xlayers; i++) {
const XLayerEncConfig *xl = &cfg->xlayers[i];
if (xl->view_type == VIEW_EXPLICIT) {
if (xl->view_id < 0) {
fprintf(stderr,
"Error: xlayer %d has view_type \"explicit\" but no view_id\n",
i);
return -1;
}
if (xl->view_id > 255) {
fprintf(stderr, "Error: xlayer %d has invalid view_id %d (0-255)\n", i,
xl->view_id);
return -1;
}
} else if (xl->view_id != -1) {
fprintf(stderr,
"Warning: xlayer %d sets view_id %d but view_type is not "
"\"explicit\"; view_id is not signalled\n",
i, xl->view_id);
}
}

// Validate input source names are unique and non-empty
for (int i = 0; i < cfg->num_input_sources; i++) {
if (cfg->input_sources[i].name[0] == '\0') {
Expand Down
Loading
Loading