CI params per embedded layer and add dependency inheritance
diff --git a/av2/common/av2_common_int.h b/av2/common/av2_common_int.h
index 1e156a0..f26a056 100644
--- a/av2/common/av2_common_int.h
+++ b/av2/common/av2_common_int.h
@@ -2659,11 +2659,16 @@
   /*!
    * Elements part of the content interpretation, when present, applicable for
    * all the frames in the video.
-   *
-   * TODO: AVM issue #1130 - Allow different CI OBUs in different embedded
-   * layers of the same bitstream.
    */
-  ContentInterpretation ci_params;
+  ContentInterpretation ci_params_per_layer[MAX_NUM_MLAYERS];
+
+  /*!
+   * Content Interpretation params. Used by the encoder only.
+   *
+   * NOTE: This field is provided for the encoder as a convenience. Eventually
+   * we should migrate the encoder to use the ci_params_per_layer array.
+   */
+  ContentInterpretation ci_params_encoder;
 #endif  // CONFIG_CWG_F270_CI_OBU
 
   /*!
diff --git a/av2/decoder/decodeframe.c b/av2/decoder/decodeframe.c
index 82e612f..62cb1b9 100644
--- a/av2/decoder/decodeframe.c
+++ b/av2/decoder/decodeframe.c
@@ -3926,7 +3926,9 @@
 static AVM_INLINE void setup_tip_frame_size(AV2_COMMON *cm) {
   const SequenceHeader *const seq_params = &cm->seq_params;
 #if CONFIG_CWG_F270_CI_OBU
-  const ColorInfo *const color_info = &cm->ci_params.color_info;
+  const ContentInterpretation *const ci_params =
+      &cm->ci_params_per_layer[cm->mlayer_id];
+  const ColorInfo *const color_info = &ci_params->color_info;
 #endif  // CONFIG_CWG_F270_CI_OBU
   YV12_BUFFER_CONFIG *tip_frame_buf = &cm->tip_ref.tip_frame->buf;
   if (avm_realloc_frame_buffer(
@@ -3952,9 +3954,9 @@
 #endif  // CONFIG_CWG_F270_CI_OBU
     tip_frame_buf->monochrome = seq_params->monochrome;
 #if CONFIG_CWG_F270_CI_OBU
-    if (cm->ci_params.ci_chroma_sample_position_present_flag)
+    if (ci_params->ci_chroma_sample_position_present_flag)
       tip_frame_buf->chroma_sample_position =
-          cm->ci_params.ci_chroma_sample_position[0];
+          ci_params->ci_chroma_sample_position[0];
     else
       tip_frame_buf->chroma_sample_position = AVM_CSP_UNSPECIFIED;
     tip_frame_buf->color_range = color_info->full_range_flag;
@@ -3990,9 +3992,9 @@
 #endif  // CONFIG_CWG_F270_CI_OBU
     tip_frame_buf->monochrome = seq_params->monochrome;
 #if CONFIG_CWG_F270_CI_OBU
-    if (cm->ci_params.ci_chroma_sample_position_present_flag)
+    if (ci_params->ci_chroma_sample_position_present_flag)
       tip_frame_buf->chroma_sample_position =
-          cm->ci_params.ci_chroma_sample_position[0];
+          ci_params->ci_chroma_sample_position[0];
     else
       tip_frame_buf->chroma_sample_position = AVM_CSP_UNSPECIFIED;
     tip_frame_buf->color_range = color_info->full_range_flag;
@@ -4009,7 +4011,9 @@
   BufferPool *const pool = cm->buffer_pool;
   const SequenceHeader *const seq_params = &cm->seq_params;
 #if CONFIG_CWG_F270_CI_OBU
-  const ColorInfo *const color_info = &cm->ci_params.color_info;
+  const ContentInterpretation *const ci_params =
+      &cm->ci_params_per_layer[cm->mlayer_id];
+  const ColorInfo *const color_info = &ci_params->color_info;
 #endif  // CONFIG_CWG_F270_CI_OBU
 
   lock_buffer_pool(pool);
@@ -4038,9 +4042,9 @@
 #endif  // CONFIG_CWG_F270_CI_OBU
   cm->cur_frame->buf.monochrome = seq_params->monochrome;
 #if CONFIG_CWG_F270_CI_OBU
-  if (cm->ci_params.ci_chroma_sample_position_present_flag)
+  if (ci_params->ci_chroma_sample_position_present_flag)
     cm->cur_frame->buf.chroma_sample_position =
-        cm->ci_params.ci_chroma_sample_position[0];
+        ci_params->ci_chroma_sample_position[0];
   else
     cm->cur_frame->buf.chroma_sample_position = AVM_CSP_UNSPECIFIED;
   cm->cur_frame->buf.color_range = color_info->full_range_flag;
@@ -8559,8 +8563,10 @@
 #if !CONFIG_CWG_F430
   if (seq_params->decoder_model_info_present_flag &&
 #if CONFIG_CWG_F270_CI_OBU
-      (cm->ci_params.ci_timing_info_present_flag == 1) &&
-      cm->ci_params.timing_info.equal_elemental_interval == 0
+      (cm->ci_params_per_layer[cm->mlayer_id].ci_timing_info_present_flag ==
+       1) &&
+      cm->ci_params_per_layer[cm->mlayer_id]
+              .timing_info.equal_elemental_interval == 0
 #else
       seq_params->timing_info.equal_picture_interval == 0
 #endif  // CONFIG_CWG_F270_CI_OBU
@@ -8754,22 +8760,31 @@
   }
 
 #if CONFIG_CWG_F270_CI_OBU
-  if (!pbi->ci_params_received) {
-    cm->ci_params.color_info.color_description_idc = 0;
-    cm->ci_params.color_info.color_primaries = AVM_CICP_CP_UNSPECIFIED;
-    cm->ci_params.color_info.matrix_coefficients = AVM_CICP_MC_UNSPECIFIED;
-    cm->ci_params.color_info.transfer_characteristics = AVM_CICP_TC_UNSPECIFIED;
-    cm->ci_params.color_info.full_range_flag = 0;
+  if ((obu_type == OBU_CLK || obu_type == OBU_OLK) &&
+      (pbi->ci_and_key_per_layer[cm->mlayer_id] == 0)) {
+    ContentInterpretation *ci_params = &cm->ci_params_per_layer[cm->mlayer_id];
+    ci_params->color_info.color_description_idc = 0;
+    ci_params->color_info.color_primaries = AVM_CICP_CP_UNSPECIFIED;
+    ci_params->color_info.matrix_coefficients = AVM_CICP_MC_UNSPECIFIED;
+    ci_params->color_info.transfer_characteristics = AVM_CICP_TC_UNSPECIFIED;
+    ci_params->color_info.full_range_flag = 0;
 
-    cm->ci_params.ci_chroma_sample_position[0] = AVM_CSP_UNSPECIFIED;
-    cm->ci_params.ci_chroma_sample_position[1] = AVM_CSP_UNSPECIFIED;
+    ci_params->ci_chroma_sample_position[0] = AVM_CSP_UNSPECIFIED;
+    ci_params->ci_chroma_sample_position[1] = AVM_CSP_UNSPECIFIED;
 
-    cm->ci_params.ci_scan_type_idc = 0;
-    cm->ci_params.ci_color_description_present_flag = 0;
-    cm->ci_params.ci_chroma_sample_position_present_flag = 0;
-    cm->ci_params.ci_aspect_ratio_info_present_flag = 0;
-    cm->ci_params.ci_timing_info_present_flag = 0;
-    cm->ci_params.ci_extension_present_flag = 0;
+    ci_params->ci_scan_type_idc = 0;
+    ci_params->ci_color_description_present_flag = 0;
+    ci_params->ci_chroma_sample_position_present_flag = 0;
+    ci_params->ci_aspect_ratio_info_present_flag = 0;
+    ci_params->ci_timing_info_present_flag = 0;
+    ci_params->ci_extension_present_flag = 0;
+    for (int ref_layer_id = 0; ref_layer_id < cm->mlayer_id; ref_layer_id++) {
+      if (cm->seq_params.mlayer_dependency_map[cm->mlayer_id][ref_layer_id]) {
+        cm->ci_params_per_layer[cm->mlayer_id] =
+            cm->ci_params_per_layer[ref_layer_id];
+        break;
+      }
+    }
   }
 #endif  // CONFIG_CWG_F270_CI_OBU
 
@@ -8962,7 +8977,9 @@
   AV2_COMMON *const cm = &pbi->common;
   const SequenceHeader *const seq_params = &cm->seq_params;
 #if CONFIG_CWG_F270_CI_OBU
-  const ColorInfo *const color_info = &cm->ci_params.color_info;
+  const ContentInterpretation *const ci_params =
+      &cm->ci_params_per_layer[cm->mlayer_id];
+  const ColorInfo *const color_info = &ci_params->color_info;
 #endif  // CONFIG_CWG_F270_CI_OBU
   CurrentFrame *const current_frame = &cm->current_frame;
   FeatureFlags *const features = &cm->features;
@@ -9068,17 +9085,6 @@
     }
   }
 #endif  // CONFIG_LCR_ID_IN_SH
-#if CONFIG_CWG_F270_CI_OBU
-  if (pbi->ci_params_received &&
-      (cm->ci_params.color_info.matrix_coefficients == AVM_CICP_MC_IDENTITY ||
-       cm->ci_params.color_info.matrix_coefficients == AVM_CICP_MC_YCGCO_RE ||
-       cm->ci_params.color_info.matrix_coefficients == AVM_CICP_MC_YCGCO_RO) &&
-      (cm->seq_params.subsampling_x || cm->seq_params.subsampling_y)) {
-    avm_internal_error(
-        &cm->error, AVM_CODEC_UNSUP_BITSTREAM,
-        "Matrix coefficients incompatible with non 4:4:4 color sampling.");
-  }
-#endif  // CONFIG_CWG_F270_CI_OBU
 
   if (seq_params->single_picture_header_flag) {
     cm->show_existing_frame = 0;
@@ -9264,8 +9270,8 @@
     if ((cm->show_frame || cm->showable_frame) &&
         seq_params->decoder_model_info_present_flag &&
 #if CONFIG_CWG_F270_CI_OBU
-        (cm->ci_params.ci_timing_info_present_flag == 1) &&
-        cm->ci_params.timing_info.equal_elemental_interval == 0
+        (ci_params->ci_timing_info_present_flag == 1) &&
+        ci_params->timing_info.equal_elemental_interval == 0
 #else
         seq_params->timing_info.equal_picture_interval == 0
 #endif  // CONFIG_CWG_F270_CI_OBU
@@ -10102,9 +10108,9 @@
 #endif  // CONFIG_CWG_F270_CI_OBU
   cm->cur_frame->buf.monochrome = seq_params->monochrome;
 #if CONFIG_CWG_F270_CI_OBU
-  if (cm->ci_params.ci_chroma_sample_position_present_flag)
+  if (ci_params->ci_chroma_sample_position_present_flag)
     cm->cur_frame->buf.chroma_sample_position =
-        cm->ci_params.ci_chroma_sample_position[0];
+        ci_params->ci_chroma_sample_position[0];
   else
     cm->cur_frame->buf.chroma_sample_position = AVM_CSP_UNSPECIFIED;
   cm->cur_frame->buf.color_range = color_info->full_range_flag;
@@ -10131,9 +10137,9 @@
 #endif  // CONFIG_CWG_F270_CI_OBU
   tip_frame_buf->monochrome = seq_params->monochrome;
 #if CONFIG_CWG_F270_CI_OBU
-  if (cm->ci_params.ci_chroma_sample_position_present_flag)
+  if (ci_params->ci_chroma_sample_position_present_flag)
     tip_frame_buf->chroma_sample_position =
-        cm->ci_params.ci_chroma_sample_position[0];
+        ci_params->ci_chroma_sample_position[0];
   else
     tip_frame_buf->chroma_sample_position = AVM_CSP_UNSPECIFIED;
   tip_frame_buf->color_range = color_info->full_range_flag;
diff --git a/av2/decoder/decoder.c b/av2/decoder/decoder.c
index 8346d16..9a62e0f 100644
--- a/av2/decoder/decoder.c
+++ b/av2/decoder/decoder.c
@@ -219,13 +219,7 @@
   pbi->common.buffer_pool = pool;
 
   cm->seq_params.bit_depth = AVM_BITS_8;
-#if CONFIG_CWG_F270_CI_OBU
-  cm->ci_params.ci_chroma_sample_position_present_flag = 0;
-  if (!cm->ci_params.ci_chroma_sample_position_present_flag) {
-    cm->ci_params.ci_chroma_sample_position[0] = AVM_CSP_UNSPECIFIED;
-    cm->ci_params.ci_chroma_sample_position[1] = AVM_CSP_UNSPECIFIED;
-  }
-#else
+#if !CONFIG_CWG_F270_CI_OBU
   cm->seq_params.chroma_sample_position = AVM_CSP_UNSPECIFIED;
 #endif  // CONFIG_CWG_F270_CI_OBU
 
@@ -281,22 +275,24 @@
 
 #if CONFIG_CWG_F270_CI_OBU
   // Initialize the Content Interpretation parameters
-  pbi->ci_params_received = 0;
-  cm->ci_params.color_info.color_description_idc = 0;
-  cm->ci_params.color_info.color_primaries = AVM_CICP_CP_UNSPECIFIED;
-  cm->ci_params.color_info.matrix_coefficients = AVM_CICP_MC_UNSPECIFIED;
-  cm->ci_params.color_info.transfer_characteristics = AVM_CICP_TC_UNSPECIFIED;
-  cm->ci_params.color_info.full_range_flag = 0;
-
-  cm->ci_params.ci_chroma_sample_position[0] = AVM_CSP_UNSPECIFIED;
-  cm->ci_params.ci_chroma_sample_position[1] = AVM_CSP_UNSPECIFIED;
-
-  cm->ci_params.ci_scan_type_idc = AVM_SCAN_TYPE_UNSPECIFIED;
-  cm->ci_params.ci_color_description_present_flag = 0;
-  cm->ci_params.ci_chroma_sample_position_present_flag = 0;
-  cm->ci_params.ci_aspect_ratio_info_present_flag = 0;
-  cm->ci_params.ci_timing_info_present_flag = 0;
-  cm->ci_params.ci_extension_present_flag = 0;
+  for (int i = 0; i < MAX_NUM_MLAYERS; i++) {
+    pbi->ci_and_key_per_layer[i] = 0;
+    ContentInterpretation *ci_params = &cm->ci_params_per_layer[i];
+    ci_params->ci_chroma_sample_position_present_flag = 0;
+    ci_params->ci_chroma_sample_position[0] = AVM_CSP_UNSPECIFIED;
+    ci_params->ci_chroma_sample_position[1] = AVM_CSP_UNSPECIFIED;
+    ci_params->color_info.color_description_idc = 0;
+    ci_params->color_info.color_primaries = AVM_CICP_CP_UNSPECIFIED;
+    ci_params->color_info.matrix_coefficients = AVM_CICP_MC_UNSPECIFIED;
+    ci_params->color_info.transfer_characteristics = AVM_CICP_TC_UNSPECIFIED;
+    ci_params->color_info.full_range_flag = 0;
+    ci_params->ci_scan_type_idc = AVM_SCAN_TYPE_UNSPECIFIED;
+    ci_params->ci_color_description_present_flag = 0;
+    ci_params->ci_chroma_sample_position_present_flag = 0;
+    ci_params->ci_aspect_ratio_info_present_flag = 0;
+    ci_params->ci_timing_info_present_flag = 0;
+    ci_params->ci_extension_present_flag = 0;
+  }
 #endif  // CONFIG_CWG_F270_CI_OBU
 
 #if DEBUG_EXTQUANT
diff --git a/av2/decoder/decoder.h b/av2/decoder/decoder.h
index cb8e5c5..6e3e82e 100644
--- a/av2/decoder/decoder.h
+++ b/av2/decoder/decoder.h
@@ -487,10 +487,12 @@
 #endif  // CONFIG_F153_FGM_OBU
 #if CONFIG_CWG_F270_CI_OBU
   /*!
-   * indicates if content interpretation params (in cm->ci_params) were received
-   * and are valid
+   * Indicates if the ci obu is signalled with a CLK/OLK in the temporal unit
+   * 0. CLK/OLK signalled without CI
+   * 1. CI obu signalled without CLK/OLK
+   * 2. CI obu signalled with CLK/OLK
    */
-  int ci_params_received;
+  int ci_and_key_per_layer[MAX_NUM_MLAYERS];
 #endif  // CONFIG_CWG_F270_CI_OBU
 } AV2Decoder;
 
diff --git a/av2/decoder/obu.c b/av2/decoder/obu.c
index d4fec3a..06d0fb7 100644
--- a/av2/decoder/obu.c
+++ b/av2/decoder/obu.c
@@ -495,12 +495,13 @@
         seq_params->op_params[i].decoder_model_param_present_flag = 0;
       }
 #if CONFIG_CWG_F270_CI_OBU
-      if (seq_params->op_params[i].decoder_model_param_present_flag) {
+      if (seq_params->op_params[i].decoder_model_param_present_flag)
 #else
       if (seq_params->timing_info_present &&
           (seq_params->timing_info.equal_picture_interval ||
-           seq_params->op_params[i].decoder_model_param_present_flag)) {
+           seq_params->op_params[i].decoder_model_param_present_flag))
 #endif  // CONFIG_CWG_F270_CI_OBU
+      {
         seq_params->op_params[i].bitrate = av2_max_level_bitrate(
             seq_params->profile, seq_params->seq_level_idx[i],
             seq_params->tier[i]);
@@ -667,9 +668,6 @@
       cm->quant_params.qmatrix_initialized = false;
 #endif  // !CONFIG_F255_QMOBU
       reset_mfh_valid(cm);
-#if CONFIG_CWG_F270_CI_OBU
-      pbi->ci_params_received = 0;
-#endif  // CONFIG_CWG_F270_CI_OBU
     }
   }
 #if CONFIG_F024_KEYOBU
@@ -1744,6 +1742,39 @@
 #endif  // OBU_ORDER_IN_TU
 
 #if CONFIG_F024_KEYOBU
+int av2_ci_keyframe_in_temporal_unit(struct AV2Decoder *pbi,
+                                     const uint8_t *data, size_t data_sz) {
+  const uint8_t *data_read = data;
+
+  ObuHeader obu_header;
+  memset(&obu_header, 0, sizeof(obu_header));
+  int keyframe_present = 0;
+  int ci_present = 0;
+  while (data_read < data + data_sz) {
+    size_t payload_size = 0;
+    size_t bytes_read = 0;
+    avm_read_obu_header_and_size(data_read, data_sz, &obu_header, &payload_size,
+                                 &bytes_read);
+    keyframe_present =
+        (obu_header.type == OBU_CLK || obu_header.type == OBU_OLK);
+    ci_present = (obu_header.type == OBU_CONTENT_INTERPRETATION);
+    data_read += bytes_read + payload_size;
+  }
+
+  //  * 0. CLK/OLK signalled without CI
+  //  * 1. CI obu signalled without CLK/OLK
+  //  * 2. CI obu signalled with CLK/OLK
+  if (!ci_present && keyframe_present)
+    pbi->ci_and_key_per_layer[obu_header.obu_mlayer_id] = 0;
+  else if (ci_present && !keyframe_present)
+    pbi->ci_and_key_per_layer[obu_header.obu_mlayer_id] = 1;
+  else if (ci_present && keyframe_present)
+    pbi->ci_and_key_per_layer[obu_header.obu_mlayer_id] = 2;
+  return 0;
+}
+#endif
+
+#if CONFIG_F024_KEYOBU
 int av2_is_random_accessed_temporal_unit(const uint8_t *data, size_t data_sz) {
   const uint8_t *data_read = data;
 
@@ -1804,7 +1835,9 @@
   // grain models signalled before a coded frame have the same fgm_id
   uint32_t acc_fgm_id_bitmap = 0;
 #endif  // CONFIG_F153_FGM_OBU
-
+#if CONFIG_CWG_F270_CI_OBU
+  av2_ci_keyframe_in_temporal_unit(pbi, data, data_end - data);
+#endif  // CONFIG_CWG_F270_CI_OBU
   int prev_obu_xlayer_id = -1;
 
   // decode frame as a series of OBUs
diff --git a/av2/decoder/obu_ci.c b/av2/decoder/obu_ci.c
index 2b57133..3d70f1a 100644
--- a/av2/decoder/obu_ci.c
+++ b/av2/decoder/obu_ci.c
@@ -25,6 +25,7 @@
 #include "av2/decoder/obu.h"
 #include "av2/common/av2_common_int.h"
 #if CONFIG_CWG_F270_CI_OBU
+
 static void av2_set_color_info(ContentInterpretation *ci_params) {
   assert(ci_params->color_info.color_description_idc !=
          AVM_COLOR_DESC_IDC_EXPLICIT);
@@ -164,59 +165,60 @@
   }
 }
 
-// TODO: AVM issue #1129 - Check that all instances of a CI OBU in an embedded
-// layer shall contain the same information.
 uint32_t av2_read_content_interpretation_obu(struct AV2Decoder *pbi,
                                              struct avm_read_bit_buffer *rb) {
+  // TODO: AVM issue #1172 - Add layer-dependency-based inheritance of CI params
   AV2_COMMON *const cm = &pbi->common;
+  const int obu_mlayer_id = cm->mlayer_id;
   const uint32_t saved_bit_offset = rb->bit_offset;
   cm->error.error_code = AVM_CODEC_OK;
   assert(rb->error_handler);
-  ContentInterpretation *ci_params = &cm->ci_params;
-  ci_params->ci_scan_type_idc = avm_rb_read_literal(rb, 2);
-  ci_params->ci_color_description_present_flag = avm_rb_read_bit(rb);
-  ci_params->ci_chroma_sample_position_present_flag = avm_rb_read_bit(rb);
-  ci_params->ci_aspect_ratio_info_present_flag = avm_rb_read_bit(rb);
-  ci_params->ci_timing_info_present_flag = avm_rb_read_bit(rb);
-  ci_params->ci_extension_present_flag = avm_rb_read_bit(rb);
+
+  // Parse CI OBU into a temp structure
+  ContentInterpretation ci_temp;
+  ci_temp.ci_scan_type_idc = avm_rb_read_literal(rb, 2);
+  ci_temp.ci_color_description_present_flag = avm_rb_read_bit(rb);
+  ci_temp.ci_chroma_sample_position_present_flag = avm_rb_read_bit(rb);
+  ci_temp.ci_aspect_ratio_info_present_flag = avm_rb_read_bit(rb);
+  ci_temp.ci_timing_info_present_flag = avm_rb_read_bit(rb);
+  ci_temp.ci_extension_present_flag = avm_rb_read_bit(rb);
   (void)avm_rb_read_bit(rb);  // ci_reserved_1bit
 
-  if (ci_params->ci_color_description_present_flag) {
-    av2_read_color_info(ci_params, rb);
-    if (ci_params->color_info.color_description_idc !=
-        AVM_COLOR_DESC_IDC_EXPLICIT)
-      av2_set_color_info(ci_params);
+  if (ci_temp.ci_color_description_present_flag) {
+    av2_read_color_info(&ci_temp, rb);
+    if (ci_temp.color_info.color_description_idc != AVM_COLOR_DESC_IDC_EXPLICIT)
+      av2_set_color_info(&ci_temp);
   } else {
-    ci_params->color_info.color_primaries = AVM_CICP_CP_UNSPECIFIED;
-    ci_params->color_info.matrix_coefficients = AVM_CICP_MC_UNSPECIFIED;
-    ci_params->color_info.transfer_characteristics = AVM_CICP_TC_UNSPECIFIED;
-    ci_params->color_info.full_range_flag = 0;
+    ci_temp.color_info.color_primaries = AVM_CICP_CP_UNSPECIFIED;
+    ci_temp.color_info.matrix_coefficients = AVM_CICP_MC_UNSPECIFIED;
+    ci_temp.color_info.transfer_characteristics = AVM_CICP_TC_UNSPECIFIED;
+    ci_temp.color_info.full_range_flag = 0;
   }
 
-  if (ci_params->ci_chroma_sample_position_present_flag) {
-    ci_params->ci_chroma_sample_position[0] = avm_rb_read_uvlc(rb);
-    if (ci_params->ci_scan_type_idc != 1)
-      ci_params->ci_chroma_sample_position[1] = avm_rb_read_uvlc(rb);
+  if (ci_temp.ci_chroma_sample_position_present_flag) {
+    ci_temp.ci_chroma_sample_position[0] = avm_rb_read_uvlc(rb);
+    if (ci_temp.ci_scan_type_idc != 1)
+      ci_temp.ci_chroma_sample_position[1] = avm_rb_read_uvlc(rb);
     else
-      ci_params->ci_chroma_sample_position[1] =
-          ci_params->ci_chroma_sample_position[0];
+      ci_temp.ci_chroma_sample_position[1] =
+          ci_temp.ci_chroma_sample_position[0];
   } else {
-    ci_params->ci_chroma_sample_position[0] = AVM_CSP_UNSPECIFIED;
-    ci_params->ci_chroma_sample_position[1] = AVM_CSP_UNSPECIFIED;
+    ci_temp.ci_chroma_sample_position[0] = AVM_CSP_UNSPECIFIED;
+    ci_temp.ci_chroma_sample_position[1] = AVM_CSP_UNSPECIFIED;
   }
 
-  if (ci_params->ci_aspect_ratio_info_present_flag) {
-    av2_read_sample_aspect_ratio_information(ci_params, rb);
-    if (!av2_set_sar_info(ci_params)) {
+  if (ci_temp.ci_aspect_ratio_info_present_flag) {
+    av2_read_sample_aspect_ratio_information(&ci_temp, rb);
+    if (!av2_set_sar_info(&ci_temp)) {
       avm_internal_error(&cm->error, AVM_CODEC_UNSUP_BITSTREAM,
                          "Incorrect SAR values");
     }
   }
 
-  if (ci_params->ci_timing_info_present_flag)
-    av2_read_timing_info_header(&ci_params->timing_info, &cm->error, rb);
+  if (ci_temp.ci_timing_info_present_flag)
+    av2_read_timing_info_header(&ci_temp.timing_info, &cm->error, rb);
 
-  if (ci_params->ci_extension_present_flag) {
+  if (ci_temp.ci_extension_present_flag) {
     // TODO: issue #1111 - Add the extension mechanism
   }
 
@@ -225,7 +227,15 @@
     return 0;
   }
 
-  pbi->ci_params_received = 1;
+  //  * 0. CLK/OLK signalled without CI
+  //  * 1. CI obu signalled without CLK/OLK
+  //  * 2. CI obu signalled with CLK/OLK
+  if (pbi->ci_and_key_per_layer[obu_mlayer_id] == 1) {
+    avm_internal_error(&cm->error, AVM_CODEC_CORRUPT_FRAME,
+                       "CI OBU is signalled without CLK/OLK");
+  } else if (pbi->ci_and_key_per_layer[obu_mlayer_id] == 2) {
+    cm->ci_params_per_layer[obu_mlayer_id] = ci_temp;
+  }
 
   return ((rb->bit_offset - saved_bit_offset + 7) >> 3);
 }
diff --git a/av2/encoder/bitstream.c b/av2/encoder/bitstream.c
index 47f5284..b03a0ba 100644
--- a/av2/encoder/bitstream.c
+++ b/av2/encoder/bitstream.c
@@ -6401,7 +6401,7 @@
 #if !CONFIG_CWG_F430
   if (seq_params->decoder_model_info_present_flag &&
 #if CONFIG_CWG_F270_CI_OBU
-      cm->ci_params.timing_info.equal_elemental_interval == 0) {
+      cm->ci_params_encoder.timing_info.equal_elemental_interval == 0) {
 #else
       seq_params->timing_info.equal_picture_interval == 0) {
 #endif  // CONFIG_CWG_F270_CI_OBU
@@ -6547,7 +6547,7 @@
     if ((cm->show_frame || cm->showable_frame) &&
         seq_params->decoder_model_info_present_flag &&
 #if CONFIG_CWG_F270_CI_OBU
-        cm->ci_params.timing_info.equal_elemental_interval == 0
+        cm->ci_params_encoder.timing_info.equal_elemental_interval == 0
 #else
         seq_params->timing_info.equal_picture_interval == 0
 #endif  // CONFIG_CWG_F270_CI_OBU
@@ -8595,7 +8595,7 @@
       obu_header_size = av2_write_obu_header(
           level_params, OBU_CONTENT_INTERPRETATION, 0, 0, data);
       obu_payload_size = av2_write_content_interpretation_obu(
-          &cm->ci_params, data + obu_header_size);
+          &cm->ci_params_encoder, data + obu_header_size);
       size_t length_field_size1 =
           obu_memmove(obu_header_size, obu_payload_size, data);
       if (av2_write_uleb_obu_size(obu_header_size, obu_payload_size, data) !=
@@ -9018,8 +9018,8 @@
 #if CONFIG_CWG_F430
   int write_temporal_point_metadata =
       (cpi->write_ci_obu_flag &&
-       cpi->common.ci_params.ci_timing_info_present_flag &&
-       cpi->common.ci_params.timing_info.equal_elemental_interval == 0)
+       cpi->common.ci_params_encoder.ci_timing_info_present_flag &&
+       cpi->common.ci_params_encoder.timing_info.equal_elemental_interval == 0)
           ? 1
           : 0;
   if (write_temporal_point_metadata) {
diff --git a/av2/encoder/encoder.c b/av2/encoder/encoder.c
index d0064e9..6f5c828 100644
--- a/av2/encoder/encoder.c
+++ b/av2/encoder/encoder.c
@@ -765,7 +765,7 @@
   const DecoderModelCfg *const dec_model_cfg = &oxcf->dec_model_cfg;
   const ColorCfg *const color_cfg = &oxcf->color_cfg;
   cpi->oxcf = *oxcf;
-  ContentInterpretation *ci_params = &cpi->common.ci_params;
+  ContentInterpretation *ci_params = &cpi->common.ci_params_encoder;
 
   // Scan type information
   cpi->scan_type_info_present_flag = oxcf->tool_cfg.scan_type_info_present_flag;
@@ -4227,7 +4227,7 @@
 
   SequenceHeader *const seq_params = &cm->seq_params;
 #if CONFIG_CWG_F270_CI_OBU
-  ColorInfo *const color_info = &cm->ci_params.color_info;
+  ColorInfo *const color_info = &cm->ci_params_encoder.color_info;
 #endif  // CONFIG_CWG_F270_CI_OBU
   if (cm->bru.enabled && cm->current_frame.frame_type != KEY_FRAME) {
     enc_bru_swap_stage(cpi);
@@ -4259,7 +4259,7 @@
   cm->cur_frame->buf.monochrome = seq_params->monochrome;
 #if CONFIG_CWG_F270_CI_OBU
   cm->cur_frame->buf.chroma_sample_position =
-      cm->ci_params.ci_chroma_sample_position[0];
+      cm->ci_params_encoder.ci_chroma_sample_position[0];
   cm->cur_frame->buf.color_range = color_info->full_range_flag;
 #else
   cm->cur_frame->buf.chroma_sample_position =
@@ -4849,7 +4849,7 @@
       break;
   }
 #if CONFIG_CWG_F270_CI_OBU
-  cm->ci_params.ci_timing_info_present_flag &=
+  cm->ci_params_encoder.ci_timing_info_present_flag &=
       !seq_params->single_picture_header_flag;
 #else
   seq_params->timing_info_present &= !seq_params->single_picture_header_flag;
diff --git a/av2/encoder/encoder_utils.c b/av2/encoder/encoder_utils.c
index ee0ba76..fae0492 100644
--- a/av2/encoder/encoder_utils.c
+++ b/av2/encoder/encoder_utils.c
@@ -506,7 +506,8 @@
         reset_film_grain_chroma_params(&cm->film_grain_params);
       cm->film_grain_params.bit_depth = cm->seq_params.bit_depth;
 #if CONFIG_CWG_F270_CI_OBU
-      if (cm->ci_params.color_info.full_range_flag == AVM_CR_FULL_RANGE) {
+      if (cm->ci_params_encoder.color_info.full_range_flag ==
+          AVM_CR_FULL_RANGE) {
 #else
       if (cm->seq_params.color_range == AVM_CR_FULL_RANGE) {
 #endif  // CONFIG_CWG_F270_CI_OBU
@@ -514,7 +515,8 @@
       }
 #if CONFIG_FGS_IDENT
 #if CONFIG_CWG_F270_CI_OBU
-      if (cm->ci_params.color_info.matrix_coefficients == AVM_CICP_MC_IDENTITY)
+      if (cm->ci_params_per_layer[cm->mlayer_id]
+              .color_info.matrix_coefficients == AVM_CICP_MC_IDENTITY)
 #else
       if (cm->seq_params.matrix_coefficients == AVM_CICP_MC_IDENTITY)
 #endif  // CONFIG_CWG_F270_CI_OBU
diff --git a/av2/encoder/level.c b/av2/encoder/level.c
index 2bf7ef2..4fd294d 100644
--- a/av2/encoder/level.c
+++ b/av2/encoder/level.c
@@ -527,9 +527,9 @@
   dfg_interval_queue->size = 0;
 
 #if CONFIG_CWG_F270_CI_OBU
-  if (cm->ci_params.ci_timing_info_present_flag) {
+  if (cm->ci_params_encoder.ci_timing_info_present_flag) {
     decoder_model->num_ticks_per_picture =
-        cm->ci_params.timing_info.num_ticks_per_elemental_duration;
+        cm->ci_params_encoder.timing_info.num_ticks_per_elemental_duration;
 #else
   if (seq_params->timing_info_present) {
     decoder_model->num_ticks_per_picture =
@@ -537,8 +537,8 @@
 #endif  // CONFIG_CWG_F270_CI_OBU
     decoder_model->display_clock_tick =
 #if CONFIG_CWG_F270_CI_OBU
-        cm->ci_params.timing_info.num_ticks_per_elemental_duration /
-        cm->ci_params.timing_info.time_scale;
+        cm->ci_params_encoder.timing_info.num_ticks_per_elemental_duration /
+        cm->ci_params_encoder.timing_info.time_scale;
 #else
         seq_params->timing_info.num_units_in_display_tick /
         seq_params->timing_info.time_scale;