GOP mostly working!

Need to be careful of fwd kfs, and order hint modolus.

Change-Id: I45348a483c0fd68a1911aed57b2a42011e439191
diff --git a/CMakeLists.txt b/CMakeLists.txt
index df67220..5f38dea 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -165,13 +165,17 @@
             "${AOM_ROOT}/common/rawenc.c"
             "${AOM_ROOT}/common/rawenc.h"
             "${AOM_ROOT}/common/y4menc.c"
-            "${AOM_ROOT}/common/y4menc.h")
-
-list(APPEND AOM_DECODER_APP_UTIL_SOURCES "${AOM_ROOT}/common/ivfdec.c"
+            "${AOM_ROOT}/common/y4menc.h"
+            "${AOM_ROOT}/common/ivfdec.c"
             "${AOM_ROOT}/common/ivfdec.h" "${AOM_ROOT}/common/obudec.c"
             "${AOM_ROOT}/common/obudec.h" "${AOM_ROOT}/common/video_reader.c"
             "${AOM_ROOT}/common/video_reader.h")
 
+# list(APPEND AOM_DECODER_APP_UTIL_SOURCES "${AOM_ROOT}/common/ivfdec.c"
+#             "${AOM_ROOT}/common/ivfdec.h" "${AOM_ROOT}/common/obudec.c"
+#             "${AOM_ROOT}/common/obudec.h" "${AOM_ROOT}/common/video_reader.c"
+#             "${AOM_ROOT}/common/video_reader.h")
+
 list(APPEND AOM_ENCODER_APP_UTIL_SOURCES
             "${AOM_ROOT}/common/ivfenc.c"
             "${AOM_ROOT}/common/ivfenc.h"
@@ -323,11 +327,11 @@
 #
 if(ENABLE_EXAMPLES OR ENABLE_TESTS OR ENABLE_TOOLS)
   add_library(aom_common_app_util OBJECT ${AOM_COMMON_APP_UTIL_SOURCES})
-  if(CONFIG_AV1_DECODER)
-    add_library(aom_decoder_app_util OBJECT ${AOM_DECODER_APP_UTIL_SOURCES})
-    # obudec depends on internal headers that require *rtcd.h
-    add_dependencies(aom_decoder_app_util aom_rtcd)
-  endif()
+  # if(CONFIG_AV1_DECODER)
+  #   add_library(aom_decoder_app_util OBJECT ${AOM_DECODER_APP_UTIL_SOURCES})
+  #   # obudec depends on internal headers that require *rtcd.h
+  #   add_dependencies(aom_decoder_app_util aom_rtcd)
+  # endif()
   if(CONFIG_AV1_ENCODER)
     add_library(aom_encoder_app_util OBJECT ${AOM_ENCODER_APP_UTIL_SOURCES})
   endif()
@@ -335,31 +339,38 @@
 
 if((CONFIG_AV1_DECODER OR CONFIG_AV1_ENCODER) AND ENABLE_EXAMPLES)
   add_executable(resize_util "${AOM_ROOT}/examples/resize_util.c"
-                             $<TARGET_OBJECTS:aom_common_app_util>)
+    $<TARGET_OBJECTS:aom_common_app_util>
+    $<TARGET_OBJECTS:aom_encoder_app_util>)
   list(APPEND AOM_APP_TARGETS resize_util)
 endif()
 
 if(CONFIG_AV1_DECODER AND ENABLE_EXAMPLES)
   add_executable(aomdec "${AOM_ROOT}/apps/aomdec.c"
                         $<TARGET_OBJECTS:aom_common_app_util>
-                        $<TARGET_OBJECTS:aom_decoder_app_util>)
+                        # $<TARGET_OBJECTS:aom_decoder_app_util>
+                        )
   add_executable(decode_to_md5 "${AOM_ROOT}/examples/decode_to_md5.c"
                                $<TARGET_OBJECTS:aom_common_app_util>
-                               $<TARGET_OBJECTS:aom_decoder_app_util>)
+                               # $<TARGET_OBJECTS:aom_decoder_app_util>
+                               )
   add_executable(decode_with_drops "${AOM_ROOT}/examples/decode_with_drops.c"
                                    $<TARGET_OBJECTS:aom_common_app_util>
-                                   $<TARGET_OBJECTS:aom_decoder_app_util>)
+                                   # $<TARGET_OBJECTS:aom_decoder_app_util>
+                                   )
   add_executable(simple_decoder "${AOM_ROOT}/examples/simple_decoder.c"
                                 $<TARGET_OBJECTS:aom_common_app_util>
-                                $<TARGET_OBJECTS:aom_decoder_app_util>)
+                                # $<TARGET_OBJECTS:aom_decoder_app_util>
+                                )
   add_executable(scalable_decoder "${AOM_ROOT}/examples/scalable_decoder.c"
                                   $<TARGET_OBJECTS:aom_common_app_util>
-                                  $<TARGET_OBJECTS:aom_decoder_app_util>)
+                                  # $<TARGET_OBJECTS:aom_decoder_app_util>
+                                  )
 
   if(CONFIG_ANALYZER)
     add_executable(analyzer "${AOM_ROOT}/examples/analyzer.cc"
                             $<TARGET_OBJECTS:aom_common_app_util>
-                            $<TARGET_OBJECTS:aom_decoder_app_util>)
+                            # $<TARGET_OBJECTS:aom_decoder_app_util>
+                            )
     target_link_libraries(analyzer ${AOM_LIB_LINK_TYPE} ${wxWidgets_LIBRARIES})
     list(APPEND AOM_APP_TARGETS analyzer)
     list(APPEND AOM_DECODER_EXAMPLE_TARGETS analyzer)
@@ -368,7 +379,8 @@
   if(CONFIG_INSPECTION)
     add_executable(inspect "${AOM_ROOT}/examples/inspect.c"
                            $<TARGET_OBJECTS:aom_common_app_util>
-                           $<TARGET_OBJECTS:aom_decoder_app_util>)
+                           # $<TARGET_OBJECTS:aom_decoder_app_util>
+                           )
     list(APPEND AOM_DECODER_EXAMPLE_TARGETS inspect)
 
     if(EMSCRIPTEN)
@@ -567,7 +579,8 @@
                             "${AOM_ROOT}/tools/obu_parser.cc"
                             "${AOM_ROOT}/tools/obu_parser.h"
                             $<TARGET_OBJECTS:aom_common_app_util>
-                            $<TARGET_OBJECTS:aom_decoder_app_util>)
+                            # $<TARGET_OBJECTS:aom_decoder_app_util>
+                            )
 
     list(APPEND AOM_TOOL_TARGETS dump_obu)
     list(APPEND AOM_APP_TARGETS dump_obu)
@@ -600,7 +613,8 @@
   add_executable(lightfield_tile_list_decoder
                  "${AOM_ROOT}/examples/lightfield_tile_list_decoder.c"
                  $<TARGET_OBJECTS:aom_common_app_util>
-                 $<TARGET_OBJECTS:aom_decoder_app_util>)
+                 # $<TARGET_OBJECTS:aom_decoder_app_util>
+                 )
   list(APPEND AOM_EXAMPLE_TARGETS lightfield_tile_list_decoder)
   list(APPEND AOM_APP_TARGETS lightfield_tile_list_decoder)
 endif()
@@ -608,7 +622,8 @@
 if(ENABLE_EXAMPLES AND CONFIG_AV1_DECODER)
   add_executable(lightfield_decoder "${AOM_ROOT}/examples/lightfield_decoder.c"
                                     $<TARGET_OBJECTS:aom_common_app_util>
-                                    $<TARGET_OBJECTS:aom_decoder_app_util>)
+                                    # $<TARGET_OBJECTS:aom_decoder_app_util>
+                                    )
   list(APPEND AOM_EXAMPLE_TARGETS lightfield_decoder)
   list(APPEND AOM_APP_TARGETS lightfield_decoder)
 endif()
@@ -618,7 +633,8 @@
                  "${AOM_ROOT}/examples/lightfield_bitstream_parsing.c"
                  $<TARGET_OBJECTS:aom_common_app_util>
                  $<TARGET_OBJECTS:aom_encoder_app_util>
-                 $<TARGET_OBJECTS:aom_decoder_app_util>)
+                 # $<TARGET_OBJECTS:aom_decoder_app_util>
+                 )
   list(APPEND AOM_EXAMPLE_TARGETS lightfield_bitstream_parsing)
   list(APPEND AOM_APP_TARGETS lightfield_bitstream_parsing)
 endif()
@@ -647,10 +663,11 @@
     endif()
 
     # Add to existing targets.
-    if(CONFIG_AV1_DECODER)
-      target_sources(aom_decoder_app_util PRIVATE ${AOM_WEBM_DECODER_SOURCES})
-    endif()
+    # if(CONFIG_AV1_DECODER)
+    #   target_sources(aom_decoder_app_util PRIVATE ${AOM_WEBM_DECODER_SOURCES})
+    # endif()
 
+    target_sources(aom_common_app_util PRIVATE ${AOM_WEBM_DECODER_SOURCES})
     if(CONFIG_AV1_ENCODER)
       target_sources(aom_encoder_app_util PRIVATE ${AOM_WEBM_ENCODER_SOURCES})
     endif()
diff --git a/aom/aomdx.h b/aom/aomdx.h
index b3fd90e..2f70a40 100644
--- a/aom/aomdx.h
+++ b/aom/aomdx.h
@@ -425,6 +425,10 @@
   /*!\brief Codec control function to get the S_FRAME coding information
    */
   AOMD_GET_S_FRAME_INFO,
+
+  AOMD_GET_SHOW_FRAME_FLAG,
+  AOMD_GET_BASE_Q_IDX,
+  AOMD_GET_ORDER_HINT,
 };
 
 /*!\cond */
diff --git a/av1/av1.cmake b/av1/av1.cmake
index f538b2b..6554ff4 100644
--- a/av1/av1.cmake
+++ b/av1/av1.cmake
@@ -240,6 +240,8 @@
             "${AOM_ROOT}/av1/encoder/svc_layercontext.h"
             "${AOM_ROOT}/av1/encoder/temporal_filter.c"
             "${AOM_ROOT}/av1/encoder/temporal_filter.h"
+            "${AOM_ROOT}/av1/encoder/thirdpass.c"
+            "${AOM_ROOT}/av1/encoder/thirdpass.h"
             "${AOM_ROOT}/av1/encoder/tokenize.c"
             "${AOM_ROOT}/av1/encoder/tokenize.h"
             "${AOM_ROOT}/av1/encoder/tpl_model.c"
diff --git a/av1/av1_dx_iface.c b/av1/av1_dx_iface.c
index 02968ab..c576998 100644
--- a/av1/av1_dx_iface.c
+++ b/av1/av1_dx_iface.c
@@ -1381,6 +1381,49 @@
   return AOM_CODEC_INVALID_PARAM;
 }
 
+static aom_codec_err_t ctrl_get_base_q_idx(aom_codec_alg_priv_t *ctx,
+                                           va_list args) {
+  unsigned int *const base_q = va_arg(args, unsigned int *);
+
+  if (base_q) {
+    AVxWorker *const worker = ctx->frame_worker;
+    if (worker) {
+      FrameWorkerData *const frame_worker_data =
+          (FrameWorkerData *)worker->data1;
+      *base_q = frame_worker_data->pbi->common.quant_params.base_qindex;
+      return AOM_CODEC_OK;
+    } else {
+      return AOM_CODEC_ERROR;
+    }
+  }
+  return AOM_CODEC_INVALID_PARAM;
+}
+
+static aom_codec_err_t ctrl_get_show_frame_flag(aom_codec_alg_priv_t *ctx,
+                                                va_list args) {
+  int *const arg = va_arg(args, int *);
+  if (arg == NULL) return AOM_CODEC_INVALID_PARAM;
+  if (ctx->frame_worker == NULL) return AOM_CODEC_ERROR;
+  *arg = ((FrameWorkerData *)ctx->frame_worker->data1)->pbi->common.show_frame;
+  return AOM_CODEC_OK;
+}
+
+static aom_codec_err_t ctrl_get_order_hint(aom_codec_alg_priv_t *ctx,
+                                           va_list args) {
+  int *const arg = va_arg(args, int *);
+  if (arg == NULL) return AOM_CODEC_INVALID_PARAM;
+  if (ctx->frame_worker == NULL) return AOM_CODEC_ERROR;
+  if (0 && ((FrameWorkerData *)ctx->frame_worker->data1)
+          ->pbi->common.show_existing_frame) {
+    *arg = ((FrameWorkerData *)ctx->frame_worker->data1)
+               ->pbi->common.cur_frame->order_hint;
+  } else {
+    *arg = ((FrameWorkerData *)ctx->frame_worker->data1)
+               ->pbi->common.current_frame.order_hint;
+  }
+  return AOM_CODEC_OK;
+}
+
 static aom_codec_err_t ctrl_set_invert_tile_order(aom_codec_alg_priv_t *ctx,
                                                   va_list args) {
   ctx->invert_tile_order = va_arg(args, int);
@@ -1569,6 +1612,9 @@
   { AOMD_GET_SHOW_EXISTING_FRAME_FLAG, ctrl_get_show_existing_frame_flag },
   { AOMD_GET_S_FRAME_INFO, ctrl_get_s_frame_info },
 
+  { AOMD_GET_SHOW_FRAME_FLAG, ctrl_get_show_frame_flag },
+  { AOMD_GET_BASE_Q_IDX, ctrl_get_base_q_idx },
+  { AOMD_GET_ORDER_HINT, ctrl_get_order_hint },
   CTRL_MAP_END,
 };
 
diff --git a/av1/decoder/decodeframe.c b/av1/decoder/decodeframe.c
index 9ca7d3c..da3ce26 100644
--- a/av1/decoder/decodeframe.c
+++ b/av1/decoder/decodeframe.c
@@ -4547,6 +4547,7 @@
       // assign_frame_buffer_p()!
       assert(!cm->cur_frame->raw_frame_buffer.data);
       assign_frame_buffer_p(&cm->cur_frame, frame_to_show);
+      cm->current_frame.order_hint = cm->cur_frame->order_hint;
       pbi->reset_decoder_state = frame_to_show->frame_type == KEY_FRAME;
       unlock_buffer_pool(pool);
 
diff --git a/av1/encoder/encoder.c b/av1/encoder/encoder.c
index afeec65..c0955cf 100644
--- a/av1/encoder/encoder.c
+++ b/av1/encoder/encoder.c
@@ -16,6 +16,7 @@
 #include <time.h>
 #include <stdlib.h>
 
+#include "av1/encoder/thirdpass.h"
 #include "config/aom_config.h"
 #include "config/aom_dsp_rtcd.h"
 
@@ -1435,6 +1436,10 @@
 #endif
   cm->error->setjmp = 0;
 
+  
+  cpi->third_pass_ctx = NULL;
+  av1_init_thirdpass_ctx(&cpi->third_pass_ctx, "./temp.ivf");
+
   return cpi;
 }
 
@@ -1579,6 +1584,8 @@
 #endif
   }
 
+  av1_free_thirdpass_ctx(cpi->third_pass_ctx);
+
   dealloc_compressor_data(cpi);
 
   av1_ext_part_delete(&cpi->ext_part_controller);
diff --git a/av1/encoder/encoder.h b/av1/encoder/encoder.h
index fe6e76f..4122bb1 100644
--- a/av1/encoder/encoder.h
+++ b/av1/encoder/encoder.h
@@ -47,6 +47,7 @@
 #include "av1/encoder/speed_features.h"
 #include "av1/encoder/svc_layercontext.h"
 #include "av1/encoder/temporal_filter.h"
+#include "av1/encoder/thirdpass.h"
 #include "av1/encoder/tokenize.h"
 #include "av1/encoder/tpl_model.h"
 #include "av1/encoder/av1_noise_estimate.h"
@@ -2920,6 +2921,8 @@
    */
   bool do_frame_data_update;
 #endif
+
+  THIRD_PASS_DEC_CTX *third_pass_ctx;
 } AV1_COMP;
 
 /*!
diff --git a/av1/encoder/firstpass.h b/av1/encoder/firstpass.h
index 122912f..668667f 100644
--- a/av1/encoder/firstpass.h
+++ b/av1/encoder/firstpass.h
@@ -29,6 +29,7 @@
 #define MIN_MV_IN_OUT 0.4
 
 #define VLOW_MOTION_THRESHOLD 950
+struct ThreadData;
 
 /*!
  * \brief The stucture of acummulated frame stats in the first pass.
diff --git a/av1/encoder/pass2_strategy.c b/av1/encoder/pass2_strategy.c
index e3639f7..dd0e2ab 100644
--- a/av1/encoder/pass2_strategy.c
+++ b/av1/encoder/pass2_strategy.c
@@ -3802,7 +3802,8 @@
       cpi->ppi->gf_state.arf_gf_boost_lst = 0;
     }
 
-    // TODO(jingning): Resoleve the redundant calls here.
+    av1_set_gop_third_pass(cpi->third_pass_ctx, NULL);
+    // TODO(jingning): Resolve the redundant calls here.
     if (rc->intervals_till_gf_calculate_due == 0 || 1) {
       calculate_gf_length(cpi, max_gop_length, MAX_NUM_GF_INTERVALS);
     }
diff --git a/av1/encoder/thirdpass.c b/av1/encoder/thirdpass.c
new file mode 100644
index 0000000..181a65d
--- /dev/null
+++ b/av1/encoder/thirdpass.c
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#include "aom/aom_codec.h"
+#include "aom/aomdx.h"
+#include "av1/encoder/firstpass.h"
+#include "av1/encoder/thirdpass.h"
+#include "av1/common/blockd.h"
+#include "common/video_reader.h"
+
+static int open_file(THIRD_PASS_DEC_CTX *ctx) {
+  if (ctx->file == NULL) {
+    die("No third pass input specified. ");
+  }
+  ctx->reader = aom_video_reader_open(ctx->file);
+  if (!ctx->reader) die("Failed to open %s for third pass.", ctx->file);
+
+  aom_codec_iface_t *decoder =
+      get_aom_decoder_by_fourcc(ctx->reader->info.codec_fourcc);
+  if (!decoder) die("Unknown input codec.");
+  if (aom_codec_dec_init(&ctx->codec, decoder, NULL, 0))
+    die("Failed to initialize decoder.");
+  return 0;
+}
+
+static int read_frame(THIRD_PASS_DEC_CTX *ctx) {
+  if (ctx->reader == NULL) {
+    open_file(ctx);
+  }
+  if (!ctx->have_frame) {
+    if (!aom_video_reader_read_frame(ctx->reader)) return EXIT_FAILURE;
+    ctx->frame = aom_video_reader_get_frame(ctx->reader, &ctx->frame_size);
+    ctx->end_frame = ctx->frame + ctx->frame_size;
+    ctx->have_frame = 1;
+  }
+  Av1DecodeReturn adr;
+  if (aom_codec_decode(&ctx->codec, ctx->frame, (unsigned int)ctx->frame_size,
+                       &adr) != AOM_CODEC_OK) {
+    die_codec(&ctx->codec, "Failed to decode frame for third pass.");
+  }
+  ctx->frame = adr.buf;
+  ctx->frame_size = ctx->end_frame - ctx->frame;
+  if (ctx->frame == ctx->end_frame) ctx->have_frame = 0;
+  return 0;
+}
+
+static int get_frame_gop_info(THIRD_PASS_DEC_CTX *ctx) {
+  int cur = ctx->num_gop_info_left;
+  int frame_type_flags = 0;
+  if (aom_codec_control(&ctx->codec, AOMD_GET_FRAME_FLAGS, &frame_type_flags) !=
+      AOM_CODEC_OK) {
+    die("failed to read frame flags.");
+  }
+  if (frame_type_flags & AOM_FRAME_IS_KEY) {
+    ctx->frame_info[cur].frame_type = KEY_FRAME;
+  } else if (frame_type_flags & AOM_FRAME_IS_INTRAONLY) {
+    ctx->frame_info[cur].frame_type = INTRA_ONLY_FRAME;
+  } else if (frame_type_flags & AOM_FRAME_IS_SWITCH) {
+    ctx->frame_info[cur].frame_type = S_FRAME;
+  } else {
+    ctx->frame_info[cur].frame_type = INTER_FRAME;
+  }
+
+  if (aom_codec_control(&ctx->codec, AOMD_GET_BASE_Q_IDX,
+                        &ctx->frame_info[cur].base_q_idx) != AOM_CODEC_OK) {
+    die("failed to read base q index.");
+  }
+
+  if (aom_codec_control(&ctx->codec, AOMD_GET_SHOW_EXISTING_FRAME_FLAG,
+                        &ctx->frame_info[cur].is_show_existing) !=
+      AOM_CODEC_OK) {
+    die("failed to read show existing frame flag.");
+  }
+
+  if (aom_codec_control(&ctx->codec, AOMD_GET_SHOW_FRAME_FLAG,
+                        &ctx->frame_info[cur].is_show_frame) != AOM_CODEC_OK) {
+    die("failed to read show frame flag.");
+  }
+
+  if (aom_codec_control(&ctx->codec, AOMD_GET_ORDER_HINT,
+                        &ctx->frame_info[cur].order_hint) != AOM_CODEC_OK) {
+    die("failed to read order hint.");
+  }
+  ctx->num_gop_info_left++;
+  return 0;
+}
+
+// TODO(bohanli): define an enum for this return
+// return :
+// -1: failed
+// 1: no arf
+// 2: with arf
+static int read_gop_frames(THIRD_PASS_DEC_CTX *ctx, int max_num,
+                           int *last_idx) {
+  *last_idx = 0;
+  int cur_idx = 0;
+  int arf_order_hint = -1;
+  // int kf_offset = 0;
+  // int has_kf = 0;
+  int num_show_frames = 0;
+  while (num_show_frames < max_num) {
+    // read in from bitstream if needed
+    if (cur_idx >= ctx->num_gop_info_left) {
+      read_frame(ctx);
+      get_frame_gop_info(ctx);
+    }
+
+    // TODO(bohanli): verify that fwd_kf works here.
+    if (ctx->frame_info[cur_idx].frame_type == KEY_FRAME &&
+        ctx->frame_info[cur_idx].is_show_frame == 1) {
+      // if this is a key frame,
+      if (cur_idx != 0) {
+        // we have reached the next key frame. Stop here.
+        *last_idx = cur_idx - 1;
+        return 1;
+      }
+    } else if (ctx->frame_info[cur_idx].is_show_frame == 0 &&
+               arf_order_hint == -1) {
+      // if this is an arf (the first no show)
+      if (num_show_frames <= 1) {
+        // this is an arf and we should end the GOP with its overlay
+        arf_order_hint = ctx->frame_info[cur_idx].order_hint;
+      } else {
+        // we treat the frames previous to this arf as a gop.
+        *last_idx = cur_idx - 1;
+        return 1;
+      }
+    } else if (arf_order_hint >= 0 &&
+               ctx->frame_info[cur_idx].order_hint == arf_order_hint) {
+      // if this is the overlay/show existing of the arf
+      assert(ctx->frame_info[cur_idx].is_show_frame);
+      *last_idx = cur_idx;
+      return 2;
+    } else {
+      // this frame is part of the GOP.
+      if (ctx->frame_info[cur_idx].is_show_frame) num_show_frames++;
+    }
+    cur_idx++;
+  }
+  assert(arf_order_hint < 0);
+  *last_idx = max_num - 1;
+  return 1;
+}
+
+int av1_set_gop_third_pass(THIRD_PASS_DEC_CTX *ctx, GF_GROUP *gf_group) {
+  (void)gf_group;
+  // Read in future frames and find the last frame in the GOP
+  int last_idx;
+  read_gop_frames(ctx, 32, &last_idx);
+
+  // TODO(bohanli):Define and set the GOP structure
+  printf("\narf length is %d\n",
+         (ctx->frame_info[last_idx].order_hint - ctx->last_end_gop) % (1 << 7));
+
+  ctx->last_end_gop = ctx->frame_info[last_idx].order_hint;
+
+  // Reset the frame info ptr to the next frame.
+  ctx->num_gop_info_left -= (last_idx + 1);
+  for (int i = 0; i < ctx->num_gop_info_left; i++) {
+    ctx->frame_info[i] = ctx->frame_info[i + last_idx + 1];
+  }
+  return 0;
+}
+
+void av1_init_thirdpass_ctx(THIRD_PASS_DEC_CTX **ctx, char *file) {
+  if (*ctx == NULL) {
+    *ctx = aom_malloc(sizeof(**ctx));
+  }
+  THIRD_PASS_DEC_CTX *ctx_ptr = *ctx;
+  ctx_ptr->file = file;
+  ctx_ptr->reader = NULL;
+  ctx_ptr->num_gop_info_left = 0;
+  ctx_ptr->last_end_gop = -1;
+  ctx_ptr->have_frame = 0;
+}
+
+void av1_free_thirdpass_ctx(THIRD_PASS_DEC_CTX *ctx) {
+  if (ctx == NULL) return;
+  if (ctx->codec.iface && aom_codec_destroy(&ctx->codec)) {
+    die("Failded to destroy decoder codec for thirdpass.");
+  }
+  aom_video_reader_close(ctx->reader);
+  ctx->reader = NULL;
+  ctx->num_gop_info_left = 0;
+  aom_free(ctx);
+}
diff --git a/av1/encoder/thirdpass.h b/av1/encoder/thirdpass.h
new file mode 100644
index 0000000..272884a
--- /dev/null
+++ b/av1/encoder/thirdpass.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2021, Alliance for Open Media. All rights reserved
+ *
+ * This source code is subject to the terms of the BSD 2 Clause License and
+ * the Alliance for Open Media Patent License 1.0. If the BSD 2 Clause License
+ * was not distributed with this source code in the LICENSE file, you can
+ * obtain it at www.aomedia.org/license/software. If the Alliance for Open
+ * Media Patent License 1.0 was not distributed with this source code in the
+ * PATENTS file, you can obtain it at www.aomedia.org/license/patent.
+ */
+
+#ifndef AOM_AV1_ENCODER_THIRDPASS_H_
+#define AOM_AV1_ENCODER_THIRDPASS_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "av1/encoder/firstpass.h"
+#include "av1/common/blockd.h"
+#include "common/video_reader.h"
+
+// TODO(bohanli): optimize this number
+#define MAX_THIRD_PASS_BUF 100
+
+typedef struct {
+  int base_q_idx;
+  int is_show_existing;
+  int is_show_frame;
+  FRAME_TYPE frame_type;
+  int order_hint;
+} THIRD_PASS_FRAME_INFO;
+
+typedef struct {
+  char *file;
+  AvxVideoReader *reader;
+  aom_codec_ctx_t codec;
+  THIRD_PASS_FRAME_INFO frame_info[MAX_THIRD_PASS_BUF];
+  int num_gop_info_left;
+  int last_end_gop;
+  const unsigned char *end_frame;
+  const unsigned char *frame;
+  size_t frame_size;
+  int have_frame;
+} THIRD_PASS_DEC_CTX;
+
+int av1_set_gop_third_pass(THIRD_PASS_DEC_CTX *ctx, GF_GROUP *gf_group);
+void av1_init_thirdpass_ctx(THIRD_PASS_DEC_CTX **ctx, char *file);
+void av1_free_thirdpass_ctx(THIRD_PASS_DEC_CTX *ctx);
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // AOM_AV1_ENCODER_THIRDPASS_H_
diff --git a/common/video_reader.c b/common/video_reader.c
index 7b021bc..0a5f40f 100644
--- a/common/video_reader.c
+++ b/common/video_reader.c
@@ -19,17 +19,6 @@
 #include "common/video_reader.h"
 #include "common/webmdec.h"
 
-struct AvxVideoReaderStruct {
-  AvxVideoInfo info;
-  struct AvxInputContext input_ctx;
-  struct ObuDecInputContext obu_ctx;
-  struct WebmInputContext webm_ctx;
-  uint8_t *buffer;
-  size_t buffer_size;
-  size_t frame_size;
-  aom_codec_pts_t pts;
-};
-
 AvxVideoReader *aom_video_reader_open(const char *filename) {
   AvxVideoReader *reader = NULL;
   FILE *const file = fopen(filename, "rb");
diff --git a/common/video_reader.h b/common/video_reader.h
index 9ab439e..b1fc84d 100644
--- a/common/video_reader.h
+++ b/common/video_reader.h
@@ -13,13 +13,27 @@
 #define AOM_COMMON_VIDEO_READER_H_
 
 #include "common/video_common.h"
+#include "aom_ports/mem_ops.h"
+#include "common/ivfdec.h"
+#include "common/obudec.h"
+#include "common/tools_common.h"
+#include "common/webmdec.h"
 
 // The following code is work in progress. It is going to  support transparent
 // reading of input files. Right now only IVF format is supported for
 // simplicity. The main goal the API is to be simple and easy to use in example
 // code and in aomenc/aomdec later. All low-level details like memory
 // buffer management are hidden from API users.
-struct AvxVideoReaderStruct;
+struct AvxVideoReaderStruct {
+  AvxVideoInfo info;
+  struct AvxInputContext input_ctx;
+  struct ObuDecInputContext obu_ctx;
+  struct WebmInputContext webm_ctx;
+  uint8_t *buffer;
+  size_t buffer_size;
+  size_t frame_size;
+  aom_codec_pts_t pts;
+};
 typedef struct AvxVideoReaderStruct AvxVideoReader;
 
 #ifdef __cplusplus
diff --git a/test/test.cmake b/test/test.cmake
index 9ada58d..42da7fe 100644
--- a/test/test.cmake
+++ b/test/test.cmake
@@ -389,7 +389,8 @@
   list(APPEND AOM_APP_TARGETS test_libaom)
 
   if(CONFIG_AV1_DECODER)
-    target_sources(test_libaom PRIVATE $<TARGET_OBJECTS:aom_decoder_app_util>
+    target_sources(test_libaom PRIVATE
+      # $<TARGET_OBJECTS:aom_decoder_app_util>
                    $<TARGET_OBJECTS:test_aom_decoder>)
 
     if(ENABLE_DECODE_PERF_TESTS AND CONFIG_WEBM_IO)
@@ -408,7 +409,12 @@
     if(NOT BUILD_SHARED_LIBS)
       add_executable(test_intra_pred_speed
                      ${AOM_TEST_INTRA_PRED_SPEED_SOURCES}
-                     $<TARGET_OBJECTS:aom_common_app_util>)
+                     $<TARGET_OBJECTS:aom_common_app_util>
+                     # $<TARGET_OBJECTS:aom_decoder_app_util>
+                     )
+      if(CONFIG_WEBM_IO)
+        target_sources(test_intra_pred_speed PRIVATE $<TARGET_OBJECTS:webm>)
+      endif()
       target_link_libraries(test_intra_pred_speed ${AOM_LIB_LINK_TYPE} aom
                             aom_gtest)
       list(APPEND AOM_APP_TARGETS test_intra_pred_speed)