Commit 164fedb418 for aom
commit 164fedb4187c9bed9eb36c0bca4a4fadfde114a6
Author: James Zern <jzern@google.com>
Date: Sat Nov 7 21:28:14 2026 -0800
tpl_model: fix crash on early pass 2 flush
When the encoder is flushed early during pass 2 (e.g., on process
termination), the lookahead buffer may have fewer frames than the
first-pass GOP interval (`gf_intervals[] > 16`). In
`av1_tpl_setup_stats()` (`approx_gop_eval=1`),
`init_gop_frames_for_tpl()` stops once lookahead frames are exhausted,
leaving `tpl_gf_group_frames` at or below `gf_group->arf_index`.
Guard the `beta[0]`/`beta[1]` calculation in `av1_tpl_setup_stats()`
with `tpl_gf_group_frames > gf_group->arf_index` to avoid negative
indexing (`frame_idx_1 = -1`) and reading unpopulated `ARF_UPDATE`
stats. Also check `gf_frame_index` bounds, `tpl_frame->is_valid`, and
`tpl_frame->tpl_stats_ptr` in `get_frame_importance()`, returning `1.0`
when TPL stats are unavailable.
BUG=573628429
Change-Id: Ife8267ecc1f71955a7d59aee4633522ab60f801d
diff --git a/av1/encoder/tpl_model.c b/av1/encoder/tpl_model.c
index 246193f5c0..94f4771d57 100644
--- a/av1/encoder/tpl_model.c
+++ b/av1/encoder/tpl_model.c
@@ -2007,7 +2007,9 @@ static inline int skip_tpl_for_frame(const GF_GROUP *gf_group, int frame_idx,
*/
static double get_frame_importance(const TplParams *tpl_data,
int gf_frame_index) {
+ if (gf_frame_index < 0 || gf_frame_index >= MAX_TPL_FRAME_IDX) return 1.0;
const TplDepFrame *tpl_frame = &tpl_data->tpl_frame[gf_frame_index];
+ if (!tpl_frame->is_valid || tpl_frame->tpl_stats_ptr == NULL) return 1.0;
const TplDepStats *tpl_stats = tpl_frame->tpl_stats_ptr;
const int tpl_stride = tpl_frame->stride;
@@ -2293,18 +2295,20 @@ int av1_tpl_setup_stats(AV1_COMP *cpi, int approx_gop_eval,
assert(gf_group->arf_index >= 0);
double beta[2] = { 0.0 };
- const int frame_idx_0 = gf_group->arf_index;
- const int frame_idx_1 =
- AOMMIN(tpl_gf_group_frames - 1, gf_group->arf_index + 1);
- beta[0] = get_frame_importance(tpl_data, frame_idx_0);
- beta[1] = get_frame_importance(tpl_data, frame_idx_1);
- if (approx_gop_eval && cpi->oxcf.speed == 4) {
- if (frame_params->frame_type == KEY_FRAME) {
- tpl_data->tpl_stats_buffer[0].use_pred_sad =
- (cpi->ppi->p_rc.baseline_gf_interval >= 32 && beta[0] <= 1.48);
- }
- if (!tpl_data->tpl_stats_buffer[0].use_pred_sad && beta[0] <= 1.42) {
- beta[0] = 1.40;
+ if (tpl_gf_group_frames > gf_group->arf_index) {
+ const int frame_idx_0 = gf_group->arf_index;
+ const int frame_idx_1 =
+ AOMMIN(tpl_gf_group_frames - 1, gf_group->arf_index + 1);
+ beta[0] = get_frame_importance(tpl_data, frame_idx_0);
+ beta[1] = get_frame_importance(tpl_data, frame_idx_1);
+ if (approx_gop_eval && cpi->oxcf.speed == 4) {
+ if (frame_params->frame_type == KEY_FRAME) {
+ tpl_data->tpl_stats_buffer[0].use_pred_sad =
+ (cpi->ppi->p_rc.baseline_gf_interval >= 32 && beta[0] <= 1.48);
+ }
+ if (!tpl_data->tpl_stats_buffer[0].use_pred_sad && beta[0] <= 1.42) {
+ beta[0] = 1.40;
+ }
}
}
#if CONFIG_COLLECT_COMPONENT_TIMING
diff --git a/test/cancellation_test.cc b/test/cancellation_test.cc
index e0a6f991e9..cdf2e1c11f 100644
--- a/test/cancellation_test.cc
+++ b/test/cancellation_test.cc
@@ -89,6 +89,18 @@ class CancellationTest
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
}
+ void DoSimulateCancellationPass2Static(int limit, int cancel_after) {
+ ::libaom_test::DummyVideoSource raw_video;
+ raw_video.SetSize(256, 256);
+ raw_video.set_limit(limit);
+
+ CancellableVideoSource video(&raw_video);
+ video.set_cancel_after(cancel_after);
+
+ // This should not crash
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+ }
+
private:
int tile_columns_;
int tile_rows_;
@@ -104,6 +116,11 @@ TEST_P(CancellationTest, SimulateCancellationPass2Threads4) {
/*tile_rows=*/1);
}
+TEST_P(CancellationTest, SimulateCancellationPass2LongGop) {
+ DoSimulateCancellationPass2Static(/*limit=*/40, /*cancel_after=*/20);
+ DoSimulateCancellationPass2Static(/*limit=*/60, /*cancel_after=*/40);
+}
+
// Test 2-pass good quality
AV1_INSTANTIATE_TEST_SUITE(CancellationTest,
::testing::Values(::libaom_test::kTwoPassGood));