Adding timing info to sequence headers

Change-Id: I0fdb09499196e02709e067f690dff71146ee5114
diff --git a/aom/aomcx.h b/aom/aomcx.h
index 5790145..f257010 100644
--- a/aom/aomcx.h
+++ b/aom/aomcx.h
@@ -683,6 +683,12 @@
    * 0 : off, 1 : MAX_EXTREME_MV, 2 : MIN_EXTREME_MV
    */
   AV1E_ENABLE_MOTION_VECTOR_UNIT_TEST,
+
+  /*!\brief Codec control function to signal picture timing info in the
+   * bitstream. \note Valid ranges: 0..1, default is "UNKNOWN". 0 = UNKNOWN, 1 =
+   * EQUAL
+   */
+  AV1E_SET_TIMING_INFO,
 };
 
 /*!\brief aom 1-D scaling mode
@@ -753,6 +759,9 @@
   AOM_CONTENT_INVALID
 } aom_tune_content;
 
+/*!brief AV1 encoder timing info signaling */
+typedef enum { AOM_TIMING_UNSPECIFIED, AOM_TIMING_EQUAL } aom_timing_info_t;
+
 /*!\brief Model tuning parameters
  *
  * Changes the encoder to tune for certain types of input material.
@@ -857,6 +866,9 @@
 AOM_CTRL_USE_TYPE(AV1E_SET_MTU, unsigned int)
 #define AOM_CTRL_AV1E_SET_MTU
 
+AOM_CTRL_USE_TYPE(AV1E_SET_TIMING_INFO, aom_timing_info_t)
+#define AOM_CTRL_AV1E_SET_TIMING_INFO
+
 AOM_CTRL_USE_TYPE(AV1E_SET_DISABLE_TEMPMV, unsigned int)
 #define AOM_CTRL_AV1E_SET_DISABLE_TEMPMV
 
diff --git a/aom_dsp/binary_codes_reader.c b/aom_dsp/binary_codes_reader.c
index 7eba5a4..ef64b75 100644
--- a/aom_dsp/binary_codes_reader.c
+++ b/aom_dsp/binary_codes_reader.c
@@ -143,3 +143,16 @@
   const uint16_t scaled_n = (n << 1) - 1;
   return aom_rb_read_primitive_refsubexpfin(rb, scaled_n, k, ref) - n + 1;
 }
+
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+uint32_t aom_rb_read_uvlc(struct aom_read_bit_buffer *rb) {
+  int leading_zeros = 0;
+
+  while (!aom_rb_read_bit(rb)) ++leading_zeros;
+
+  uint32_t value = aom_rb_read_literal(rb, leading_zeros);
+  value += (1 << leading_zeros) - 1;
+
+  return value;
+}
+#endif
diff --git a/aom_dsp/binary_codes_reader.h b/aom_dsp/binary_codes_reader.h
index f9596c2..a27709d 100644
--- a/aom_dsp/binary_codes_reader.h
+++ b/aom_dsp/binary_codes_reader.h
@@ -46,6 +46,11 @@
 
 int16_t aom_rb_read_signed_primitive_refsubexpfin(
     struct aom_read_bit_buffer *rb, uint16_t n, uint16_t k, int16_t ref);
+
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+uint32_t aom_rb_read_uvlc(struct aom_read_bit_buffer *rb);
+#endif
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/aom_dsp/binary_codes_writer.c b/aom_dsp/binary_codes_writer.c
index 4e53b15..3db9a58 100644
--- a/aom_dsp/binary_codes_writer.c
+++ b/aom_dsp/binary_codes_writer.c
@@ -208,3 +208,17 @@
   const uint16_t scaled_n = (n << 1) - 1;
   return aom_count_primitive_refsubexpfin(scaled_n, k, ref, v);
 }
+
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+void aom_wb_write_uvlc(struct aom_write_bit_buffer *wb, uint32_t v) {
+  int64_t shift_val = ++v;
+  int leading_zeroes = 1;
+
+  assert(shift_val > 0);
+
+  while (shift_val >>= 1) leading_zeroes += 2;
+
+  aom_wb_write_literal(wb, 0, leading_zeroes >> 1);
+  aom_wb_write_unsigned_literal(wb, v, (leading_zeroes + 1) >> 1);
+}
+#endif
diff --git a/aom_dsp/binary_codes_writer.h b/aom_dsp/binary_codes_writer.h
index 6c22c4c..69d383e 100644
--- a/aom_dsp/binary_codes_writer.h
+++ b/aom_dsp/binary_codes_writer.h
@@ -60,6 +60,9 @@
                                      uint16_t v);
 int aom_count_signed_primitive_refsubexpfin(uint16_t n, uint16_t k, int16_t ref,
                                             int16_t v);
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+void aom_wb_write_uvlc(struct aom_write_bit_buffer *wb, uint32_t v);
+#endif
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/aom_dsp/bitreader_buffer.c b/aom_dsp/bitreader_buffer.c
index 1a61db1..312fa56 100644
--- a/aom_dsp/bitreader_buffer.c
+++ b/aom_dsp/bitreader_buffer.c
@@ -35,6 +35,16 @@
   return value;
 }
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+uint32_t aom_rb_read_unsigned_literal(struct aom_read_bit_buffer *rb,
+                                      int bits) {
+  uint32_t value = 0;
+  int bit;
+  for (bit = bits - 1; bit >= 0; bit--) value |= aom_rb_read_bit(rb) << bit;
+  return value;
+}
+#endif
+
 int aom_rb_read_inv_signed_literal(struct aom_read_bit_buffer *rb, int bits) {
   const int nbits = sizeof(unsigned) * 8 - bits - 1;
   const unsigned value = (unsigned)aom_rb_read_literal(rb, bits + 1) << nbits;
diff --git a/aom_dsp/bitreader_buffer.h b/aom_dsp/bitreader_buffer.h
index eeedc1f..2c583a3 100644
--- a/aom_dsp/bitreader_buffer.h
+++ b/aom_dsp/bitreader_buffer.h
@@ -37,6 +37,10 @@
 
 int aom_rb_read_literal(struct aom_read_bit_buffer *rb, int bits);
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+uint32_t aom_rb_read_unsigned_literal(struct aom_read_bit_buffer *rb, int bits);
+#endif
+
 int aom_rb_read_inv_signed_literal(struct aom_read_bit_buffer *rb, int bits);
 
 #ifdef __cplusplus
diff --git a/aom_dsp/bitwriter_buffer.c b/aom_dsp/bitwriter_buffer.c
index 1b3dd29..6601b4c 100644
--- a/aom_dsp/bitwriter_buffer.c
+++ b/aom_dsp/bitwriter_buffer.c
@@ -48,6 +48,14 @@
   for (bit = bits - 1; bit >= 0; bit--) aom_wb_write_bit(wb, (data >> bit) & 1);
 }
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+void aom_wb_write_unsigned_literal(struct aom_write_bit_buffer *wb,
+                                   uint32_t data, int bits) {
+  int bit;
+  for (bit = bits - 1; bit >= 0; bit--) aom_wb_write_bit(wb, (data >> bit) & 1);
+}
+#endif
+
 void aom_wb_overwrite_literal(struct aom_write_bit_buffer *wb, int data,
                               int bits) {
   int bit;
diff --git a/aom_dsp/bitwriter_buffer.h b/aom_dsp/bitwriter_buffer.h
index 1f23dc8..ac328d5 100644
--- a/aom_dsp/bitwriter_buffer.h
+++ b/aom_dsp/bitwriter_buffer.h
@@ -31,6 +31,11 @@
 
 void aom_wb_write_literal(struct aom_write_bit_buffer *wb, int data, int bits);
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+void aom_wb_write_unsigned_literal(struct aom_write_bit_buffer *wb,
+                                   uint32_t data, int bits);
+#endif
+
 void aom_wb_overwrite_literal(struct aom_write_bit_buffer *wb, int data,
                               int bits);
 
diff --git a/aomenc.c b/aomenc.c
index 33d7199..e9196d8 100644
--- a/aomenc.c
+++ b/aomenc.c
@@ -467,6 +467,16 @@
     ARG_DEF(NULL, "mtu-size", 1,
             "MTU size for a tile group, default is 0 (no MTU targeting), "
             "overrides maximum number of tile groups");
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+static const struct arg_enum_list timing_info_enum[] = {
+  { "unspecified", AOM_TIMING_UNSPECIFIED },
+  { "constant", AOM_TIMING_EQUAL },
+  { NULL, 0 }
+};
+static const arg_def_t timing_info =
+    ARG_DEF_ENUM(NULL, "timing-info", 1,
+                 "Signal timing info in the bitstream:", timing_info_enum);
+#endif
 #if CONFIG_TEMPMV_SIGNALING
 static const arg_def_t disable_tempmv = ARG_DEF(
     NULL, "disable-tempmv", 1, "Disable temporal mv prediction (default is 0)");
@@ -696,6 +706,9 @@
 #endif  // CONFIG_EXT_PARTITION
                                        &num_tg,
                                        &mtu_size,
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+                                       &timing_info,
+#endif
 #if CONFIG_TEMPMV_SIGNALING
                                        &disable_tempmv,
 #endif
@@ -764,6 +777,9 @@
 #endif  // CONFIG_EXT_PARTITION
                                         AV1E_SET_NUM_TG,
                                         AV1E_SET_MTU,
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+                                        AV1E_SET_TIMING_INFO,
+#endif
 #if CONFIG_TEMPMV_SIGNALING
                                         AV1E_SET_DISABLE_TEMPMV,
 #endif
@@ -2125,9 +2141,19 @@
     }
 #endif
 
-    /* Use the frame rate from the file only if none was specified
-     * on the command-line.
-     */
+/* Use the frame rate from the file only if none was specified
+ * on the command-line.
+ */
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+    if (!global.have_framerate) {
+      global.framerate.num = input.framerate.numerator;
+      global.framerate.den = input.framerate.denominator;
+    }
+    FOREACH_STREAM(stream, streams) {
+      stream->config.cfg.g_timebase.den = global.framerate.num;
+      stream->config.cfg.g_timebase.num = global.framerate.den;
+    }
+#else
     if (!global.have_framerate) {
       global.framerate.num = input.framerate.numerator;
       global.framerate.den = input.framerate.denominator;
@@ -2136,7 +2162,7 @@
         stream->config.cfg.g_timebase.num = global.framerate.den;
       }
     }
-
+#endif
     /* Show configuration */
     if (global.verbose && pass == 0) {
       FOREACH_STREAM(stream, streams) {
diff --git a/av1/av1_cx_iface.c b/av1/av1_cx_iface.c
index 8af3c14..385111d 100644
--- a/av1/av1_cx_iface.c
+++ b/av1/av1_cx_iface.c
@@ -71,6 +71,9 @@
 #endif
   unsigned int num_tg;
   unsigned int mtu_size;
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  aom_timing_info_t timing_info;
+#endif
 #if CONFIG_TEMPMV_SIGNALING
   unsigned int disable_tempmv;
 #endif
@@ -144,6 +147,9 @@
 #endif
   1,  // max number of tile groups
   0,  // mtu_size
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  AOM_TIMING_UNSPECIFIED,  // No picture timing signaling in bitstream
+#endif
 #if CONFIG_TEMPMV_SIGNALING
   0,  // disable temporal mv prediction
 #endif
@@ -424,6 +430,10 @@
   RANGE_CHECK(extra_cfg, tuning, AOM_TUNE_PSNR, AOM_TUNE_SSIM);
 #endif
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  RANGE_CHECK(extra_cfg, timing_info, AOM_TIMING_UNSPECIFIED, AOM_TIMING_EQUAL);
+#endif
+
   if (extra_cfg->lossless) {
     if (extra_cfg->aq_mode != 0)
       ERROR("Only --aq_mode=0 can be used with --lossless=1.");
@@ -497,8 +507,24 @@
   oxcf->input_bit_depth = cfg->g_input_bit_depth;
   // guess a frame rate if out of whack, use 30
   oxcf->init_framerate = (double)cfg->g_timebase.den / cfg->g_timebase.num;
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  if (extra_cfg->timing_info == AOM_TIMING_EQUAL) {
+    oxcf->timing_info_present = 1;
+    oxcf->num_units_in_tick = cfg->g_timebase.num;
+    oxcf->time_scale = cfg->g_timebase.den;
+    oxcf->equal_picture_interval = 1;
+    oxcf->num_ticks_per_picture = 1;
+  } else {
+    oxcf->timing_info_present = 0;
+  }
+  if (oxcf->init_framerate > 180) {
+    oxcf->init_framerate = 30;
+    oxcf->timing_info_present = 0;
+  }
+#else
   if (oxcf->init_framerate > 180) oxcf->init_framerate = 30;
 
+#endif
   oxcf->mode = GOOD;
 
   switch (cfg->g_pass) {
@@ -983,6 +1009,14 @@
   extra_cfg.mtu_size = CAST(AV1E_SET_MTU, args);
   return update_extra_cfg(ctx, &extra_cfg);
 }
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+static aom_codec_err_t ctrl_set_timing_info(aom_codec_alg_priv_t *ctx,
+                                            va_list args) {
+  struct av1_extracfg extra_cfg = ctx->extra_cfg;
+  extra_cfg.timing_info = CAST(AV1E_SET_TIMING_INFO, args);
+  return update_extra_cfg(ctx, &extra_cfg);
+}
+#endif
 #if CONFIG_TEMPMV_SIGNALING
 static aom_codec_err_t ctrl_set_disable_tempmv(aom_codec_alg_priv_t *ctx,
                                                va_list args) {
@@ -1682,6 +1716,9 @@
 #endif
   { AV1E_SET_NUM_TG, ctrl_set_num_tg },
   { AV1E_SET_MTU, ctrl_set_mtu },
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  { AV1E_SET_TIMING_INFO, ctrl_set_timing_info },
+#endif
 #if CONFIG_TEMPMV_SIGNALING
   { AV1E_SET_DISABLE_TEMPMV, ctrl_set_disable_tempmv },
 #endif
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index 35c2da3..5dfc526 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -167,6 +167,26 @@
   return bit_depth;
 }
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+static void parse_timing_info_header(struct aom_read_bit_buffer *rb) {
+  int timing_info_present;
+  int equal_picture_interval;
+  uint32_t num_ticks_per_picture;
+
+  timing_info_present = aom_rb_read_bit(rb);  // timing info present flag
+
+  if (timing_info_present) {
+    rb->bit_offset += 32;  // Number of units in tick
+    rb->bit_offset += 32;  // Time scale
+
+    equal_picture_interval = aom_rb_read_bit(rb);  // Equal picture interval bit
+    if (equal_picture_interval) {
+      num_ticks_per_picture = aom_rb_read_uvlc(rb) - 1;  // ticks per picture
+    }
+  }
+}
+#endif  // CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+
 static int parse_bitdepth_colorspace_sampling(BITSTREAM_PROFILE profile,
                                               struct aom_read_bit_buffer *rb) {
 #if CONFIG_CICP
@@ -259,6 +279,9 @@
 #if CONFIG_EXT_QM
   rb->bit_offset += 1;  // separate_uv_delta_q
 #endif                  // CONFIG_EXT_QM
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  parse_timing_info_header(rb);
+#endif
   return 1;
 }
 #endif
diff --git a/av1/common/onyxc_int.h b/av1/common/onyxc_int.h
index 7dff3eb..5c12805 100644
--- a/av1/common/onyxc_int.h
+++ b/av1/common/onyxc_int.h
@@ -242,6 +242,13 @@
   int render_height;
   int last_width;
   int last_height;
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  int timing_info_present;
+  uint32_t num_units_in_tick;
+  uint32_t time_scale;
+  int equal_picture_interval;
+  uint32_t num_ticks_per_picture;
+#endif
 
   // TODO(jkoleszar): this implies chroma ss right now, but could vary per
   // plane. Revisit as part of the future change to YV12_BUFFER_CONFIG to
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 025afdf..5066de1 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -2269,6 +2269,25 @@
 #endif  // CONFIG_EXT_QM
 }
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+void av1_read_timing_info_header(AV1_COMMON *cm,
+                                 struct aom_read_bit_buffer *rb) {
+  cm->timing_info_present = aom_rb_read_bit(rb);  // timing info present flag
+
+  if (cm->timing_info_present) {
+    cm->num_units_in_tick =
+        aom_rb_read_unsigned_literal(rb, 32);  // Number of units in tick
+    cm->time_scale = aom_rb_read_unsigned_literal(rb, 32);  // Time scale
+    cm->equal_picture_interval =
+        aom_rb_read_bit(rb);  // Equal picture interval bit
+    if (cm->equal_picture_interval) {
+      cm->num_ticks_per_picture =
+          aom_rb_read_uvlc(rb) + 1;  // ticks per picture
+    }
+  }
+}
+#endif
+
 #if CONFIG_REFERENCE_BUFFER || CONFIG_OBU
 void read_sequence_header(SequenceHeader *seq_params,
                           struct aom_read_bit_buffer *rb) {
@@ -2668,6 +2687,9 @@
     cm->current_video_frame = 0;
 #if !CONFIG_OBU
     av1_read_bitdepth_colorspace_sampling(cm, rb, pbi->allow_lowbitdepth);
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+    av1_read_time_info_header(cm, rb);
+#endif
 #endif
     pbi->refresh_frame_flags = (1 << REF_FRAMES) - 1;
 
@@ -2735,6 +2757,9 @@
     if (cm->intra_only) {
 #if !CONFIG_OBU
       av1_read_bitdepth_colorspace_sampling(cm, rb, pbi->allow_lowbitdepth);
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+      av1_read_timing_info_header(cm, rb);
+#endif
 #endif
 
       pbi->refresh_frame_flags = aom_rb_read_literal(rb, REF_FRAMES);
diff --git a/av1/decoder/decodeframe.h b/av1/decoder/decodeframe.h
index 9cc511d..8a4bed6 100644
--- a/av1/decoder/decodeframe.h
+++ b/av1/decoder/decodeframe.h
@@ -50,6 +50,11 @@
 void av1_read_bitdepth_colorspace_sampling(AV1_COMMON *cm,
                                            struct aom_read_bit_buffer *rb,
                                            int allow_lowbitdepth);
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+void av1_read_timing_info_header(AV1_COMMON *cm,
+                                 struct aom_read_bit_buffer *rb);
+#endif
+
 struct aom_read_bit_buffer *av1_init_read_bit_buffer(
     struct AV1Decoder *pbi, struct aom_read_bit_buffer *rb, const uint8_t *data,
     const uint8_t *data_end);
diff --git a/av1/decoder/obu.c b/av1/decoder/obu.c
index 5f8675c..2586ed5 100644
--- a/av1/decoder/obu.c
+++ b/av1/decoder/obu.c
@@ -56,6 +56,10 @@
 
   av1_read_bitdepth_colorspace_sampling(cm, rb, pbi->allow_lowbitdepth);
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  av1_read_timing_info_header(cm, rb);
+#endif
+
   return ((rb->bit_offset - saved_bit_offset + 7) >> 3);
 }
 
diff --git a/av1/encoder/bitstream.c b/av1/encoder/bitstream.c
index 418c22e..a0b5268 100644
--- a/av1/encoder/bitstream.c
+++ b/av1/encoder/bitstream.c
@@ -3289,6 +3289,25 @@
 #endif
 }
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+static void write_timing_info_header(AV1_COMMON *const cm,
+                                     struct aom_write_bit_buffer *wb) {
+  aom_wb_write_bit(wb, cm->timing_info_present);  // timing info present flag
+
+  if (cm->timing_info_present) {
+    aom_wb_write_unsigned_literal(wb, cm->num_units_in_tick,
+                                  32);  // Number of units in tick
+    aom_wb_write_unsigned_literal(wb, cm->time_scale, 32);  // Time scale
+    aom_wb_write_bit(wb,
+                     cm->equal_picture_interval);  // Equal picture interval bit
+    if (cm->equal_picture_interval) {
+      aom_wb_write_uvlc(wb,
+                        cm->num_ticks_per_picture - 1);  // ticks per picture
+    }
+  }
+}
+#endif  // CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+
 #if CONFIG_REFERENCE_BUFFER || CONFIG_OBU
 void write_sequence_header(AV1_COMP *cpi, struct aom_write_bit_buffer *wb) {
   AV1_COMMON *const cm = &cpi->common;
@@ -3547,6 +3566,10 @@
 
   if (cm->frame_type == KEY_FRAME) {
     write_bitdepth_colorspace_sampling(cm, wb);
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+    // timing_info
+    write_timing_info_header(cm, wb);
+#endif
 #if CONFIG_FRAME_SIZE
     write_frame_size(cm, frame_size_override_flag, wb);
 #else
@@ -3587,6 +3610,9 @@
 
     if (cm->intra_only) {
       write_bitdepth_colorspace_sampling(cm, wb);
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+      write_timing_info_header(cm, wb);
+#endif
 
       aom_wb_write_literal(wb, cpi->refresh_frame_mask, REF_FRAMES);
 #if CONFIG_FRAME_SIZE
@@ -4352,6 +4378,11 @@
   // color_config
   write_bitdepth_colorspace_sampling(cm, &wb);
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  // timing_info
+  write_timing_info_header(cm, &wb);
+#endif
+
   size = aom_wb_bytes_written(&wb);
   return size;
 }
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index 298ec46..a276fdc 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -1067,6 +1067,13 @@
   cm->chroma_sample_position = oxcf->chroma_sample_position;
 #endif
   cm->color_range = oxcf->color_range;
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  cm->timing_info_present = oxcf->timing_info_present;
+  cm->num_units_in_tick = oxcf->num_units_in_tick;
+  cm->time_scale = oxcf->time_scale;
+  cm->equal_picture_interval = oxcf->equal_picture_interval;
+  cm->num_ticks_per_picture = oxcf->num_ticks_per_picture;
+#endif
 
   cm->width = oxcf->width;
   cm->height = oxcf->height;
@@ -3102,6 +3109,14 @@
 
   assert(IMPLIES(cm->profile <= PROFILE_1, cm->bit_depth <= AOM_BITS_10));
 
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  cm->timing_info_present = oxcf->timing_info_present;
+  cm->num_units_in_tick = oxcf->num_units_in_tick;
+  cm->time_scale = oxcf->time_scale;
+  cm->equal_picture_interval = oxcf->equal_picture_interval;
+  cm->num_ticks_per_picture = oxcf->num_ticks_per_picture;
+#endif
+
   cpi->oxcf = *oxcf;
   x->e_mbd.bd = (int)cm->bit_depth;
   x->e_mbd.global_motion = cm->global_motion;
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index 1e47e94..a47aff9 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -294,6 +294,14 @@
   int color_range;
   int render_width;
   int render_height;
+#if CONFIG_TIMING_INFO_IN_SEQ_HEADERS
+  aom_timing_info_t timing_info;
+  int timing_info_present;
+  uint32_t num_units_in_tick;
+  uint32_t time_scale;
+  int equal_picture_interval;
+  uint32_t num_ticks_per_picture;
+#endif
 
 #if CONFIG_EXT_PARTITION
   aom_superblock_size_t superblock_size;
diff --git a/build/cmake/aom_config_defaults.cmake b/build/cmake/aom_config_defaults.cmake
index 954deb8..1ae86b9 100644
--- a/build/cmake/aom_config_defaults.cmake
+++ b/build/cmake/aom_config_defaults.cmake
@@ -163,6 +163,7 @@
 set(CONFIG_STRIPED_LOOP_RESTORATION 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_TEMPMV_SIGNALING 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_TILE_INFO_FIRST 0 CACHE NUMBER "AV1 experiment flag.")
+set(CONFIG_TIMING_INFO_IN_SEQ_HEADERS 0 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_TMV 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_TX64X64 1 CACHE NUMBER "AV1 experiment flag.")
 set(CONFIG_TXK_SEL 0 CACHE NUMBER "AV1 experiment flag.")