ref: de5a4fbb10e93e7a119ec1d5baffbc24897291a1
parent: 4745bc2ff30f5ab344202685e103a49aa56adf07
author: Marco Paniconi <[email protected]>
date: Wed Apr 11 06:59:34 EDT 2018
vp9-svc: Intra-only frame for spatial layers. Use case is for layered (SVC) coding to allow higher resolution layers to continue decoding with temporal references, while base spatial layer is intra-only frame. Made encoder changes to real-time path for encoding intra-only frame. The intra-only frame will be followed by the overlay/copy frame (with both packed in the same superframe). Use existing control to enable intra_only frame. Intra only is only applied to base spatial layer, and only allowed under fixed/non-flexible SVC mode, and only for 1 < number_spatial_layers < 4. Added svc datarate unittest for inserting intra_only frame as sync frame. Added svc end to end tests to check mismatch. Change-Id: I2f4f0106b2c4f51ce77aa2c1c6823ba83ff2f7a0 Signed-off-by: Marco Paniconi <[email protected]>
--- a/test/svc_datarate_test.cc
+++ b/test/svc_datarate_test.cc
@@ -57,6 +57,8 @@
force_key_test_ = 0;
insert_layer_sync_ = 0;
layer_sync_on_base_ = 0;
+ force_intra_only_frame_ = 0;
+ superframe_has_intra_only_ = 0;
}
virtual void BeginPassHook(unsigned int /*pass*/) {}
@@ -137,6 +139,13 @@
PreEncodeFrameHookSetup(video, encoder);
if (video->frame() == 0) {
+ if (force_intra_only_frame_) {
+ // Decoder sets the color_space for Intra-only frames
+ // to BT_601 (see line 1810 in vp9_decodeframe.c).
+ // So set it here in these tess to avoid encoder-decoder
+ // mismatch check on color space setting.
+ encoder->Control(VP9E_SET_COLOR_SPACE, VPX_CS_BT_601);
+ }
encoder->Control(VP9E_SET_NOISE_SENSITIVITY, denoiser_on_);
encoder->Control(VP9E_SET_TUNE_CONTENT, tune_content_);
encoder->Control(VP9E_SET_SVC_INTER_LAYER_PRED, inter_layer_pred_mode_);
@@ -246,19 +255,34 @@
if (insert_layer_sync_) {
vpx_svc_spatial_layer_sync_t svc_layer_sync;
svc_layer_sync.base_layer_intra_only = 0;
- layer_sync_on_base_ = 0;
for (int i = 0; i < number_spatial_layers_; i++)
svc_layer_sync.spatial_layer_sync[i] = 0;
- if (video->frame() == 150) {
- svc_layer_sync.spatial_layer_sync[1] = 1;
- encoder->Control(VP9E_SET_SVC_SPATIAL_LAYER_SYNC, &svc_layer_sync);
- } else if (video->frame() == 240) {
- svc_layer_sync.spatial_layer_sync[2] = 1;
- encoder->Control(VP9E_SET_SVC_SPATIAL_LAYER_SYNC, &svc_layer_sync);
- } else if (video->frame() == 320) {
- svc_layer_sync.spatial_layer_sync[0] = 1;
- layer_sync_on_base_ = 1;
- encoder->Control(VP9E_SET_SVC_SPATIAL_LAYER_SYNC, &svc_layer_sync);
+ if (force_intra_only_frame_) {
+ superframe_has_intra_only_ = 0;
+ if (video->frame() == 0) {
+ svc_layer_sync.base_layer_intra_only = 1;
+ svc_layer_sync.spatial_layer_sync[0] = 1;
+ encoder->Control(VP9E_SET_SVC_SPATIAL_LAYER_SYNC, &svc_layer_sync);
+ superframe_has_intra_only_ = 1;
+ } else if (video->frame() == 100) {
+ svc_layer_sync.base_layer_intra_only = 1;
+ svc_layer_sync.spatial_layer_sync[0] = 1;
+ encoder->Control(VP9E_SET_SVC_SPATIAL_LAYER_SYNC, &svc_layer_sync);
+ superframe_has_intra_only_ = 1;
+ }
+ } else {
+ layer_sync_on_base_ = 0;
+ if (video->frame() == 150) {
+ svc_layer_sync.spatial_layer_sync[1] = 1;
+ encoder->Control(VP9E_SET_SVC_SPATIAL_LAYER_SYNC, &svc_layer_sync);
+ } else if (video->frame() == 240) {
+ svc_layer_sync.spatial_layer_sync[2] = 1;
+ encoder->Control(VP9E_SET_SVC_SPATIAL_LAYER_SYNC, &svc_layer_sync);
+ } else if (video->frame() == 320) {
+ svc_layer_sync.spatial_layer_sync[0] = 1;
+ layer_sync_on_base_ = 1;
+ encoder->Control(VP9E_SET_SVC_SPATIAL_LAYER_SYNC, &svc_layer_sync);
+ }
}
}
@@ -313,7 +337,8 @@
// For test that inserts layer sync frames: requesting a layer_sync on
// the base layer must force key frame. So if any key frame occurs after
// first superframe it must due to layer sync on base spatial layer.
- if (superframe_count_ > 0 && insert_layer_sync_) {
+ if (superframe_count_ > 0 && insert_layer_sync_ &&
+ !force_intra_only_frame_) {
ASSERT_EQ(layer_sync_on_base_, 1);
}
temporal_layer_id_ = 0;
@@ -328,7 +353,13 @@
num_layers_encoded++;
}
}
- ASSERT_EQ(count, num_layers_encoded);
+ // For superframe with Intra-only count will be +1 larger
+ // because of no-show frame.
+ if (force_intra_only_frame_ && superframe_has_intra_only_)
+ ASSERT_EQ(count, num_layers_encoded + 1);
+ else
+ ASSERT_EQ(count, num_layers_encoded);
+
// In the constrained frame drop mode, if a given spatial is dropped all
// upper layers must be dropped too.
if (!layer_framedrop_) {
@@ -429,6 +460,8 @@
int inter_layer_pred_mode_;
int insert_layer_sync_;
int layer_sync_on_base_;
+ int force_intra_only_frame_;
+ int superframe_has_intra_only_;
};
// Params: speed setting.
@@ -1201,6 +1234,53 @@
ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
CheckLayerRateTargeting(number_spatial_layers_, number_temporal_layers_, 0.78,
1.15);
+#if CONFIG_VP9_DECODER
+ // The non-reference frames are expected to be mismatched frames as the
+ // encoder will avoid loopfilter on these frames.
+ EXPECT_EQ(num_nonref_frames_, GetMismatchFrames());
+#endif
+}
+
+// Run SVC encoder for 3 spatial layers, 1 temporal layer, with
+// intra-only frame as sync frame on base spatial layer.
+// Intra_only is inserted at start and in middle of sequence.
+TEST_P(DatarateOnePassCbrSvcSingleBR, OnePassCbrSvc3SL1TLSyncWithIntraOnly) {
+ cfg_.rc_buf_initial_sz = 500;
+ cfg_.rc_buf_optimal_sz = 500;
+ cfg_.rc_buf_sz = 1000;
+ cfg_.rc_min_quantizer = 0;
+ cfg_.rc_max_quantizer = 63;
+ cfg_.rc_end_usage = VPX_CBR;
+ cfg_.g_lag_in_frames = 0;
+ cfg_.ss_number_layers = 3;
+ cfg_.ts_number_layers = 1;
+ cfg_.ts_rate_decimator[0] = 1;
+ cfg_.temporal_layering_mode = 0;
+ cfg_.g_error_resilient = 1;
+ cfg_.g_threads = 4;
+ svc_params_.scaling_factor_num[0] = 72;
+ svc_params_.scaling_factor_den[0] = 288;
+ svc_params_.scaling_factor_num[1] = 144;
+ svc_params_.scaling_factor_den[1] = 288;
+ svc_params_.scaling_factor_num[2] = 288;
+ svc_params_.scaling_factor_den[2] = 288;
+ cfg_.rc_dropframe_thresh = 30;
+ cfg_.kf_max_dist = 9999;
+ cfg_.rc_target_bitrate = 400;
+ number_spatial_layers_ = cfg_.ss_number_layers;
+ number_temporal_layers_ = cfg_.ts_number_layers;
+ ::libvpx_test::I420VideoSource video("niklas_640_480_30.yuv", 640, 480, 30, 1,
+ 0, 400);
+ top_sl_width_ = 640;
+ top_sl_height_ = 480;
+ ResetModel();
+ insert_layer_sync_ = 1;
+ // Use intra_only frame for sync on base layer.
+ force_intra_only_frame_ = 1;
+ AssignLayerBitrates();
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+ CheckLayerRateTargeting(number_spatial_layers_, number_temporal_layers_, 0.73,
+ 1.2);
#if CONFIG_VP9_DECODER
// The non-reference frames are expected to be mismatched frames as the
// encoder will avoid loopfilter on these frames.
--- a/test/svc_end_to_end_test.cc
+++ b/test/svc_end_to_end_test.cc
@@ -29,7 +29,7 @@
frame_to_start_decode_(0), frame_to_sync_(0), mismatch_nframes_(0),
num_nonref_frames_(0), inter_layer_pred_mode_(GET_PARAM(1)),
decode_to_layer_before_sync_(-1), decode_to_layer_after_sync_(-1),
- denoiser_on_(0) {
+ denoiser_on_(0), intra_only_test_(false) {
SetMode(::libvpx_test::kRealTime);
memset(&svc_layer_sync_, 0, sizeof(svc_layer_sync_));
}
@@ -42,32 +42,27 @@
speed_setting_ = 7;
}
- void Set2SpatialLayerConfig() {
- SetConfig();
- cfg_.ss_number_layers = 2;
- cfg_.ts_number_layers = 3;
- svc_params_.scaling_factor_num[0] = 144;
- svc_params_.scaling_factor_den[0] = 288;
- svc_params_.scaling_factor_num[1] = 288;
- svc_params_.scaling_factor_den[1] = 288;
+ void SetSvcConfig(int num_spatial_layer, int num_temporal_layer) {
+ SetConfig(num_temporal_layer);
+ cfg_.ss_number_layers = num_spatial_layer;
+ cfg_.ts_number_layers = num_temporal_layer;
+ if (num_spatial_layer == 2) {
+ svc_params_.scaling_factor_num[0] = 144;
+ svc_params_.scaling_factor_den[0] = 288;
+ svc_params_.scaling_factor_num[1] = 288;
+ svc_params_.scaling_factor_den[1] = 288;
+ } else if (num_spatial_layer == 3) {
+ svc_params_.scaling_factor_num[0] = 72;
+ svc_params_.scaling_factor_den[0] = 288;
+ svc_params_.scaling_factor_num[1] = 144;
+ svc_params_.scaling_factor_den[1] = 288;
+ svc_params_.scaling_factor_num[2] = 288;
+ svc_params_.scaling_factor_den[2] = 288;
+ }
number_spatial_layers_ = cfg_.ss_number_layers;
number_temporal_layers_ = cfg_.ts_number_layers;
}
- void Set3SpatialLayerConfig() {
- SetConfig();
- cfg_.ss_number_layers = 3;
- cfg_.ts_number_layers = 3;
- svc_params_.scaling_factor_num[0] = 72;
- svc_params_.scaling_factor_den[0] = 288;
- svc_params_.scaling_factor_num[1] = 144;
- svc_params_.scaling_factor_den[1] = 288;
- svc_params_.scaling_factor_num[2] = 288;
- svc_params_.scaling_factor_den[2] = 288;
- number_spatial_layers_ = cfg_.ss_number_layers;
- number_temporal_layers_ = cfg_.ts_number_layers;
- }
-
virtual bool DoDecode() const {
return current_video_frame_ >= frame_to_start_decode_;
}
@@ -79,6 +74,12 @@
if (video->frame() == 0) {
encoder->Control(VP9E_SET_SVC_INTER_LAYER_PRED, inter_layer_pred_mode_);
encoder->Control(VP9E_SET_NOISE_SENSITIVITY, denoiser_on_);
+ if (intra_only_test_)
+ // Decoder sets the color_space for Intra-only frames
+ // to BT_601 (see line 1810 in vp9_decodeframe.c).
+ // So set it here in these tess to avoid encoder-decoder
+ // mismatch check on color space setting.
+ encoder->Control(VP9E_SET_COLOR_SPACE, VPX_CS_BT_601);
}
if (video->frame() == frame_to_sync_) {
encoder->Control(VP9E_SET_SVC_SPATIAL_LAYER_SYNC, &svc_layer_sync_);
@@ -127,10 +128,11 @@
int decode_to_layer_before_sync_;
int decode_to_layer_after_sync_;
int denoiser_on_;
+ bool intra_only_test_;
vpx_svc_spatial_layer_sync_t svc_layer_sync_;
private:
- void SetConfig() {
+ void SetConfig(int num_temporal_layer) {
cfg_.rc_buf_initial_sz = 500;
cfg_.rc_buf_optimal_sz = 500;
cfg_.rc_buf_sz = 1000;
@@ -142,10 +144,19 @@
cfg_.g_threads = 1;
cfg_.rc_dropframe_thresh = 30;
cfg_.kf_max_dist = 9999;
- cfg_.ts_rate_decimator[0] = 4;
- cfg_.ts_rate_decimator[1] = 2;
- cfg_.ts_rate_decimator[2] = 1;
- cfg_.temporal_layering_mode = 3;
+ if (num_temporal_layer == 3) {
+ cfg_.ts_rate_decimator[0] = 4;
+ cfg_.ts_rate_decimator[1] = 2;
+ cfg_.ts_rate_decimator[2] = 1;
+ cfg_.temporal_layering_mode = 3;
+ } else if (num_temporal_layer == 2) {
+ cfg_.ts_rate_decimator[0] = 2;
+ cfg_.ts_rate_decimator[1] = 1;
+ cfg_.temporal_layering_mode = 2;
+ } else if (num_temporal_layer == 1) {
+ cfg_.ts_rate_decimator[0] = 1;
+ cfg_.temporal_layering_mode = 1;
+ }
}
};
@@ -152,8 +163,8 @@
// Test for sync layer for 1 pass CBR SVC: 3 spatial layers and
// 3 temporal layers. Only start decoding on the sync layer.
// Full sync: insert key frame on base layer.
-TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc3SL3TLSyncFrameFull) {
- Set3SpatialLayerConfig();
+TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc3SL3TLFullSync) {
+ SetSvcConfig(3, 3);
// Sync is on base layer so the frame to sync and the frame to start decoding
// is the same.
frame_to_start_decode_ = 20;
@@ -180,8 +191,8 @@
// Test for sync layer for 1 pass CBR SVC: 2 spatial layers and
// 3 temporal layers. Decoding QVGA before sync frame and decode up to
// VGA on and after sync.
-TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc2SL3TLSyncFrameVGA) {
- Set2SpatialLayerConfig();
+TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc2SL3TLSyncToVGA) {
+ SetSvcConfig(2, 3);
frame_to_start_decode_ = 0;
frame_to_sync_ = 100;
decode_to_layer_before_sync_ = 0;
@@ -207,8 +218,8 @@
// Test for sync layer for 1 pass CBR SVC: 3 spatial layers and
// 3 temporal layers. Decoding QVGA and VGA before sync frame and decode up to
// HD on and after sync.
-TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc3SL3TLSyncFrameHD) {
- Set3SpatialLayerConfig();
+TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc3SL3TLSyncToHD) {
+ SetSvcConfig(3, 3);
frame_to_start_decode_ = 0;
frame_to_sync_ = 20;
decode_to_layer_before_sync_ = 1;
@@ -234,8 +245,8 @@
// Test for sync layer for 1 pass CBR SVC: 3 spatial layers and
// 3 temporal layers. Decoding QVGA before sync frame and decode up to
// HD on and after sync.
-TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc3SL3TLSyncFrameVGAHD) {
- Set3SpatialLayerConfig();
+TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc3SL3TLSyncToVGAHD) {
+ SetSvcConfig(3, 3);
frame_to_start_decode_ = 0;
frame_to_sync_ = 20;
decode_to_layer_before_sync_ = 0;
@@ -263,7 +274,7 @@
// 3 temporal layers. Decoding QVGA before sync frame and decode up to
// VGA on and after sync.
TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc2SL3TLSyncFrameVGADenoise) {
- Set2SpatialLayerConfig();
+ SetSvcConfig(2, 3);
frame_to_start_decode_ = 0;
frame_to_sync_ = 100;
decode_to_layer_before_sync_ = 0;
@@ -287,6 +298,64 @@
#endif
}
#endif
+
+// Start decoding from beginning of sequence, during sequence insert intra-only
+// on base/qvga layer. Decode all layers.
+TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc3SL3TLSyncFrameIntraOnlyQVGA) {
+ SetSvcConfig(3, 3);
+ frame_to_start_decode_ = 0;
+ frame_to_sync_ = 20;
+ decode_to_layer_before_sync_ = 2;
+ // The superframe containing intra-only layer will have 4 frames. Thus set the
+ // layer to decode after sync frame to 3.
+ decode_to_layer_after_sync_ = 3;
+ intra_only_test_ = true;
+
+ // Set up svc layer sync structure.
+ svc_layer_sync_.base_layer_intra_only = 1;
+ svc_layer_sync_.spatial_layer_sync[0] = 1;
+ svc_layer_sync_.spatial_layer_sync[1] = 0;
+ svc_layer_sync_.spatial_layer_sync[2] = 0;
+
+ ::libvpx_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 60);
+ cfg_.rc_target_bitrate = 600;
+ AssignLayerBitrates();
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+#if CONFIG_VP9_DECODER
+ // The non-reference frames are expected to be mismatched frames as the
+ // encoder will avoid loopfilter on these frames.
+ EXPECT_EQ(num_nonref_frames_, GetMismatchFrames());
+#endif
+}
+
+// Start decoding from beginning of sequence, during sequence insert intra-only
+// on base/qvga layer and sync_layer on middle/VGA layer. Decode all layers.
+TEST_P(SyncFrameOnePassCbrSvc, OnePassCbrSvc3SL3TLSyncFrameIntraOnlyVGA) {
+ SetSvcConfig(3, 3);
+ frame_to_start_decode_ = 0;
+ frame_to_sync_ = 20;
+ decode_to_layer_before_sync_ = 2;
+ // The superframe containing intra-only layer will have 4 frames. Thus set the
+ // layer to decode after sync frame to 3.
+ decode_to_layer_after_sync_ = 3;
+ intra_only_test_ = true;
+
+ // Set up svc layer sync structure.
+ svc_layer_sync_.base_layer_intra_only = 1;
+ svc_layer_sync_.spatial_layer_sync[0] = 1;
+ svc_layer_sync_.spatial_layer_sync[1] = 1;
+ svc_layer_sync_.spatial_layer_sync[2] = 0;
+
+ ::libvpx_test::Y4mVideoSource video("niklas_1280_720_30.y4m", 0, 60);
+ cfg_.rc_target_bitrate = 600;
+ AssignLayerBitrates();
+ ASSERT_NO_FATAL_FAILURE(RunLoop(&video));
+#if CONFIG_VP9_DECODER
+ // The non-reference frames are expected to be mismatched frames as the
+ // encoder will avoid loopfilter on these frames.
+ EXPECT_EQ(num_nonref_frames_, GetMismatchFrames());
+#endif
+}
VP9_INSTANTIATE_TEST_CASE(SyncFrameOnePassCbrSvc, ::testing::Range(0, 3));
--- a/vp9/encoder/vp9_aq_cyclicrefresh.c
+++ b/vp9/encoder/vp9_aq_cyclicrefresh.c
@@ -429,7 +429,7 @@
int thresh_low_motion = (cm->width < 720) ? 55 : 20;
int qp_thresh = VPXMIN(20, rc->best_quality << 1);
cr->apply_cyclic_refresh = 1;
- if (cm->frame_type == KEY_FRAME || cpi->svc.temporal_layer_id > 0 ||
+ if (frame_is_intra_only(cm) || cpi->svc.temporal_layer_id > 0 ||
is_lossless_requested(&cpi->oxcf) ||
rc->avg_frame_qindex[INTER_FRAME] < qp_thresh ||
(cpi->use_svc &&
--- a/vp9/encoder/vp9_encodeframe.c
+++ b/vp9/encoder/vp9_encodeframe.c
@@ -421,7 +421,7 @@
// No check for vert/horiz split as too few samples for variance.
if (bsize == bsize_min) {
// Variance already computed to set the force_split.
- if (cm->frame_type == KEY_FRAME) get_variance(&vt.part_variances->none);
+ if (frame_is_intra_only(cm)) get_variance(&vt.part_variances->none);
if (mi_col + block_width / 2 < cm->mi_cols &&
mi_row + block_height / 2 < cm->mi_rows &&
vt.part_variances->none.variance < threshold) {
@@ -431,9 +431,9 @@
return 0;
} else if (bsize > bsize_min) {
// Variance already computed to set the force_split.
- if (cm->frame_type == KEY_FRAME) get_variance(&vt.part_variances->none);
+ if (frame_is_intra_only(cm)) get_variance(&vt.part_variances->none);
// For key frame: take split for bsize above 32X32 or very high variance.
- if (cm->frame_type == KEY_FRAME &&
+ if (frame_is_intra_only(cm) &&
(bsize > BLOCK_32X32 ||
vt.part_variances->none.variance > (threshold << 4))) {
return 0;
@@ -505,7 +505,7 @@
static void set_vbp_thresholds(VP9_COMP *cpi, int64_t thresholds[], int q,
int content_state) {
VP9_COMMON *const cm = &cpi->common;
- const int is_key_frame = (cm->frame_type == KEY_FRAME);
+ const int is_key_frame = frame_is_intra_only(cm);
const int threshold_multiplier = is_key_frame ? 20 : 1;
int64_t threshold_base =
(int64_t)(threshold_multiplier * cpi->y_dequant[q][1]);
@@ -565,7 +565,7 @@
int content_state) {
VP9_COMMON *const cm = &cpi->common;
SPEED_FEATURES *const sf = &cpi->sf;
- const int is_key_frame = (cm->frame_type == KEY_FRAME);
+ const int is_key_frame = frame_is_intra_only(cm);
if (sf->partition_search_type != VAR_BASED_PARTITION &&
sf->partition_search_type != REFERENCE_PARTITION) {
return;
@@ -1226,11 +1226,11 @@
// For the variance computation under SVC mode, we treat the frame as key if
// the reference (base layer frame) is key frame (i.e., is_key_frame == 1).
int is_key_frame =
- (cm->frame_type == KEY_FRAME ||
+ (frame_is_intra_only(cm) ||
(is_one_pass_cbr_svc(cpi) &&
cpi->svc.layer_context[cpi->svc.temporal_layer_id].is_key_frame));
// Always use 4x4 partition for key frame.
- const int use_4x4_partition = cm->frame_type == KEY_FRAME;
+ const int use_4x4_partition = frame_is_intra_only(cm);
const int low_res = (cm->width <= 352 && cm->height <= 288);
int variance4x4downsample[16];
int segment_id;
@@ -1646,11 +1646,11 @@
}
}
- if (cm->frame_type != KEY_FRAME && cpi->sf.copy_partition_flag) {
+ if (!frame_is_intra_only(cm) && cpi->sf.copy_partition_flag) {
update_prev_partition(cpi, x, segment_id, mi_row, mi_col, sb_offset);
}
- if (cm->frame_type != KEY_FRAME && cpi->sf.svc_use_lowres_part &&
+ if (!frame_is_intra_only(cm) && cpi->sf.svc_use_lowres_part &&
cpi->svc.spatial_layer_id == cpi->svc.number_spatial_layers - 2)
update_partition_svc(cpi, BLOCK_64X64, mi_row, mi_col);
@@ -4366,7 +4366,7 @@
if (cyclic_refresh_segment_id_boosted(mi->segment_id))
x->rdmult = vp9_cyclic_refresh_get_rdmult(cpi->cyclic_refresh);
- if (cm->frame_type == KEY_FRAME)
+ if (frame_is_intra_only(cm))
hybrid_intra_mode_search(cpi, x, rd_cost, bsize, ctx);
else if (cpi->svc.layer_context[cpi->svc.temporal_layer_id].is_key_frame)
hybrid_search_svc_baseiskey(cpi, x, rd_cost, bsize, ctx, tile_data, mi_row,
@@ -5062,7 +5062,7 @@
// nonrd_pick_partition does not support 4x4 partition, so avoid it
// on key frame for now.
if ((cpi->oxcf.rc_mode == VPX_VBR && cpi->rc.high_source_sad &&
- cpi->oxcf.speed < 6 && cm->frame_type != KEY_FRAME &&
+ cpi->oxcf.speed < 6 && !frame_is_intra_only(cm) &&
(cpi->refresh_golden_frame || cpi->refresh_alt_ref_frame))) {
// Use lower max_partition_size for low resoultions.
if (cm->width <= 352 && cm->height <= 288)
@@ -5078,7 +5078,7 @@
// TODO(marpan): Seems like nonrd_select_partition does not support
// 4x4 partition. Since 4x4 is used on key frame, use this switch
// for now.
- if (cm->frame_type == KEY_FRAME)
+ if (frame_is_intra_only(cm))
nonrd_use_partition(cpi, td, tile_data, mi, tp, mi_row, mi_col,
BLOCK_64X64, 1, &dummy_rdc, td->pc_root);
else
--- a/vp9/encoder/vp9_encoder.c
+++ b/vp9/encoder/vp9_encoder.c
@@ -3239,6 +3239,7 @@
cpi->denoiser.denoising_level > kDenLowLow) {
int svc_refresh_denoiser_buffers = 0;
int denoise_svc_second_layer = 0;
+ FRAME_TYPE frame_type = cm->intra_only ? KEY_FRAME : cm->frame_type;
if (cpi->use_svc) {
int realloc_fail = 0;
const int svc_buf_shift =
@@ -3265,11 +3266,10 @@
"Failed to re-allocate denoiser for SVC");
}
vp9_denoiser_update_frame_info(
- &cpi->denoiser, *cpi->Source, cpi->common.frame_type,
- cpi->refresh_alt_ref_frame, cpi->refresh_golden_frame,
- cpi->refresh_last_frame, cpi->alt_fb_idx, cpi->gld_fb_idx,
- cpi->lst_fb_idx, cpi->resize_pending, svc_refresh_denoiser_buffers,
- denoise_svc_second_layer);
+ &cpi->denoiser, *cpi->Source, frame_type, cpi->refresh_alt_ref_frame,
+ cpi->refresh_golden_frame, cpi->refresh_last_frame, cpi->alt_fb_idx,
+ cpi->gld_fb_idx, cpi->lst_fb_idx, cpi->resize_pending,
+ svc_refresh_denoiser_buffers, denoise_svc_second_layer);
}
#endif
@@ -3302,6 +3302,7 @@
}
// Copy flags from encoder to SVC struct.
vp9_copy_flags_ref_update_idx(cpi);
+ vp9_svc_update_ref_frame_buffer_idx(cpi);
}
}
@@ -3969,7 +3970,7 @@
cpi->Last_Source->y_height != cpi->Source->y_height)
cpi->compute_source_sad_onepass = 0;
- if (cm->frame_type == KEY_FRAME || cpi->resize_pending != 0) {
+ if (frame_is_intra_only(cm) || cpi->resize_pending != 0) {
memset(cpi->consec_zero_mv, 0,
cm->mi_rows * cm->mi_cols * sizeof(*cpi->consec_zero_mv));
}
@@ -3993,7 +3994,7 @@
// Never drop on key frame, if base layer is key for svc,
// on scene change, or if superframe has layer sync.
if (cpi->oxcf.pass == 0 && cpi->oxcf.rc_mode == VPX_CBR &&
- cm->frame_type != KEY_FRAME && !cpi->rc.high_source_sad &&
+ !frame_is_intra_only(cm) && !cpi->rc.high_source_sad &&
!cpi->svc.high_source_sad_superframe &&
!cpi->svc.superframe_has_layer_sync &&
(!cpi->use_svc ||
@@ -4060,7 +4061,7 @@
// it may be pretty bad for rate-control,
// and I should handle it somehow
vp9_alt_ref_aq_setup_map(cpi->alt_ref_aq, cpi);
- } else if (cpi->roi.enabled && cm->frame_type != KEY_FRAME) {
+ } else if (cpi->roi.enabled && !frame_is_intra_only(cm)) {
apply_roi_map(cpi);
}
@@ -4104,7 +4105,7 @@
// Update some stats from cyclic refresh, and check for golden frame update.
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ && cm->seg.enabled &&
- cm->frame_type != KEY_FRAME)
+ !frame_is_intra_only(cm))
vp9_cyclic_refresh_postencode(cpi);
// Update the skip mb flag probabilities based on the distribution
@@ -5030,6 +5031,9 @@
if (cpi->oxcf.aq_mode == LOOKAHEAD_AQ)
vp9_alt_ref_aq_unset_all(cpi->alt_ref_aq, cpi);
+
+ cpi->svc.previous_frame_is_intra_only = cm->intra_only;
+ cpi->svc.set_intra_only_frame = 0;
}
static void SvcEncode(VP9_COMP *cpi, size_t *size, uint8_t *dest,
@@ -6023,7 +6027,7 @@
}
// Read in the source frame.
- if (cpi->use_svc)
+ if (cpi->use_svc || cpi->svc.set_intra_only_frame)
source = vp9_svc_lookahead_pop(cpi, cpi->lookahead, flush);
else
source = vp9_lookahead_pop(cpi->lookahead, flush);
--- a/vp9/encoder/vp9_pickmode.c
+++ b/vp9/encoder/vp9_pickmode.c
@@ -1781,6 +1781,11 @@
if (ref_frame > usable_ref_frame) continue;
if (skip_ref_find_pred[ref_frame]) continue;
+ if (svc->previous_frame_is_intra_only) {
+ if (ref_frame != LAST_FRAME || frame_mv[this_mode][ref_frame].as_int != 0)
+ continue;
+ }
+
// If the segment reference frame feature is enabled then do nothing if the
// current ref frame is not allowed.
if (segfeature_active(seg, mi->segment_id, SEG_LVL_REF_FRAME) &&
@@ -2308,8 +2313,9 @@
svc_force_zero_mode[best_ref_frame - 1]);
inter_mode_thresh = (inter_mode_thresh << 1) + inter_mode_thresh;
}
- if (cpi->oxcf.lag_in_frames > 0 && cpi->oxcf.rc_mode == VPX_VBR &&
- cpi->rc.is_src_frame_alt_ref)
+ if ((cpi->oxcf.lag_in_frames > 0 && cpi->oxcf.rc_mode == VPX_VBR &&
+ cpi->rc.is_src_frame_alt_ref) ||
+ svc->previous_frame_is_intra_only)
perform_intra_pred = 0;
// If the segment reference frame feature is enabled and set then
--- a/vp9/encoder/vp9_ratectrl.c
+++ b/vp9/encoder/vp9_ratectrl.c
@@ -584,9 +584,10 @@
static double get_rate_correction_factor(const VP9_COMP *cpi) {
const RATE_CONTROL *const rc = &cpi->rc;
+ const VP9_COMMON *const cm = &cpi->common;
double rcf;
- if (cpi->common.frame_type == KEY_FRAME) {
+ if (frame_is_intra_only(cm)) {
rcf = rc->rate_correction_factors[KF_STD];
} else if (cpi->oxcf.pass == 2) {
RATE_FACTOR_LEVEL rf_lvl =
@@ -606,6 +607,7 @@
static void set_rate_correction_factor(VP9_COMP *cpi, double factor) {
RATE_CONTROL *const rc = &cpi->rc;
+ const VP9_COMMON *const cm = &cpi->common;
// Normalize RCF to account for the size-dependent scaling factor.
factor /= rcf_mult[cpi->rc.frame_size_selector];
@@ -612,7 +614,7 @@
factor = fclamp(factor, MIN_BPB_FACTOR, MAX_BPB_FACTOR);
- if (cpi->common.frame_type == KEY_FRAME) {
+ if (frame_is_intra_only(cm)) {
rc->rate_correction_factors[KF_STD] = factor;
} else if (cpi->oxcf.pass == 2) {
RATE_FACTOR_LEVEL rf_lvl =
@@ -649,8 +651,9 @@
projected_size_based_on_q =
vp9_cyclic_refresh_estimate_bits_at_q(cpi, rate_correction_factor);
} else {
+ FRAME_TYPE frame_type = cm->intra_only ? KEY_FRAME : cm->frame_type;
projected_size_based_on_q =
- vp9_estimate_bits_at_q(cpi->common.frame_type, cm->base_qindex, cm->MBs,
+ vp9_estimate_bits_at_q(frame_type, cm->base_qindex, cm->MBs,
rate_correction_factor, cm->bit_depth);
}
// Work out a size correction factor.
@@ -724,8 +727,9 @@
bits_per_mb_at_this_q =
(int)vp9_cyclic_refresh_rc_bits_per_mb(cpi, i, correction_factor);
} else {
+ FRAME_TYPE frame_type = cm->intra_only ? KEY_FRAME : cm->frame_type;
bits_per_mb_at_this_q = (int)vp9_rc_bits_per_mb(
- cm->frame_type, i, correction_factor, cm->bit_depth);
+ frame_type, i, correction_factor, cm->bit_depth);
}
if (bits_per_mb_at_this_q <= target_bits_per_mb) {
@@ -822,7 +826,7 @@
int active_worst_quality;
int ambient_qp;
unsigned int num_frames_weight_key = 5 * cpi->svc.number_temporal_layers;
- if (cm->frame_type == KEY_FRAME || rc->reset_high_source_sad)
+ if (frame_is_intra_only(cm) || rc->reset_high_source_sad)
return rc->worst_quality;
// For ambient_qp we use minimum of avg_frame_qindex[KEY_FRAME/INTER_FRAME]
// for the first few frames following key frame. These are both initialized
@@ -955,7 +959,7 @@
*bottom_index = active_best_quality;
// Special case code to try and match quality with forced key frames
- if (cm->frame_type == KEY_FRAME && rc->this_key_frame_forced) {
+ if (frame_is_intra_only(cm) && rc->this_key_frame_forced) {
q = rc->last_boosted_qindex;
} else {
q = vp9_rc_regulate_q(cpi, rc->this_frame_target, active_best_quality,
@@ -1527,7 +1531,7 @@
vp9_rc_update_rate_correction_factors(cpi);
// Keep a record of last Q and ambient average Q.
- if (cm->frame_type == KEY_FRAME) {
+ if (frame_is_intra_only(cm)) {
rc->last_q[KEY_FRAME] = qindex;
rc->avg_frame_qindex[KEY_FRAME] =
ROUND_POWER_OF_TWO(3 * rc->avg_frame_qindex[KEY_FRAME] + qindex, 2);
@@ -1571,13 +1575,13 @@
(cpi->refresh_golden_frame && !rc->is_src_frame_alt_ref)))) {
rc->last_boosted_qindex = qindex;
}
- if (cm->frame_type == KEY_FRAME) rc->last_kf_qindex = qindex;
+ if (frame_is_intra_only(cm)) rc->last_kf_qindex = qindex;
update_buffer_level(cpi, rc->projected_frame_size);
// Rolling monitors of whether we are over or underspending used to help
// regulate min and Max Q in two pass.
- if (cm->frame_type != KEY_FRAME) {
+ if (!frame_is_intra_only(cm)) {
rc->rolling_target_bits = ROUND_POWER_OF_TWO(
rc->rolling_target_bits * 3 + rc->this_frame_target, 2);
rc->rolling_actual_bits = ROUND_POWER_OF_TWO(
@@ -1596,7 +1600,7 @@
if (!cpi->use_svc) {
if (is_altref_enabled(cpi) && cpi->refresh_alt_ref_frame &&
- (cm->frame_type != KEY_FRAME))
+ (!frame_is_intra_only(cm)))
// Update the alternate reference frame stats as appropriate.
update_alt_ref_frame_stats(cpi);
else
@@ -1625,7 +1629,7 @@
}
}
- if (cm->frame_type == KEY_FRAME) rc->frames_since_key = 0;
+ if (frame_is_intra_only(cm)) rc->frames_since_key = 0;
if (cm->show_frame) {
rc->frames_since_key++;
rc->frames_to_key--;
@@ -1639,7 +1643,7 @@
}
if (oxcf->pass == 0) {
- if (cm->frame_type != KEY_FRAME &&
+ if (!frame_is_intra_only(cm) &&
(!cpi->use_svc ||
(cpi->use_svc &&
!svc->layer_context[svc->temporal_layer_id].is_key_frame &&
@@ -1662,7 +1666,7 @@
}
cpi->rc.last_frame_is_src_altref = cpi->rc.is_src_frame_alt_ref;
}
- if (cm->frame_type != KEY_FRAME) rc->reset_high_source_sad = 0;
+ if (!frame_is_intra_only(cm)) rc->reset_high_source_sad = 0;
rc->last_avg_frame_bandwidth = rc->avg_frame_bandwidth;
if (cpi->use_svc && svc->spatial_layer_id < svc->number_spatial_layers - 1)
@@ -1862,6 +1866,53 @@
return vp9_rc_clamp_iframe_target_size(cpi, target);
}
+static void set_intra_only_frame(VP9_COMP *cpi) {
+ VP9_COMMON *const cm = &cpi->common;
+ SVC *const svc = &cpi->svc;
+ // Don't allow intra_only frame for bypass/flexible SVC mode, or if number
+ // of spatial layers is 1 or if number of spatial or temporal layers > 3.
+ // Also if intra-only is inserted on very first frame, don't allow if
+ // if number of temporal layers > 1. This is because on intra-only frame
+ // only 3 reference buffers can be updated, but for temporal layers > 1
+ // we generally need to use buffer slots 4 and 5.
+ if ((cm->current_video_frame == 0 && svc->number_temporal_layers > 1) ||
+ svc->temporal_layering_mode == VP9E_TEMPORAL_LAYERING_MODE_BYPASS ||
+ svc->number_spatial_layers > 3 || svc->number_temporal_layers > 3 ||
+ svc->number_spatial_layers == 1)
+ return;
+ cm->show_frame = 0;
+ cm->intra_only = 1;
+ cm->frame_type = INTER_FRAME;
+ cpi->ext_refresh_frame_flags_pending = 1;
+ cpi->ext_refresh_last_frame = 1;
+ cpi->ext_refresh_golden_frame = 1;
+ cpi->ext_refresh_alt_ref_frame = 1;
+ if (cm->current_video_frame == 0) {
+ cpi->lst_fb_idx = 0;
+ cpi->gld_fb_idx = 1;
+ cpi->alt_fb_idx = 2;
+ } else {
+ int i;
+ int count = 0;
+ cpi->lst_fb_idx = -1;
+ cpi->gld_fb_idx = -1;
+ cpi->alt_fb_idx = -1;
+ // For intra-only frame we need to refresh all slots that were
+ // being used for the base layer (fb_idx_base[i] == 1).
+ // Start with assigning last first, then golden and then alt.
+ for (i = 0; i < REF_FRAMES; ++i) {
+ if (svc->fb_idx_base[i] == 1) count++;
+ if (count == 1 && cpi->lst_fb_idx == -1) cpi->lst_fb_idx = i;
+ if (count == 2 && cpi->gld_fb_idx == -1) cpi->gld_fb_idx = i;
+ if (count == 3 && cpi->alt_fb_idx == -1) cpi->alt_fb_idx = i;
+ }
+ // If golden or alt is not being used for base layer, then set them
+ // to the lst_fb_idx.
+ if (cpi->gld_fb_idx == -1) cpi->gld_fb_idx = cpi->lst_fb_idx;
+ if (cpi->alt_fb_idx == -1) cpi->alt_fb_idx = cpi->lst_fb_idx;
+ }
+}
+
void vp9_rc_get_svc_params(VP9_COMP *cpi) {
VP9_COMMON *const cm = &cpi->common;
RATE_CONTROL *const rc = &cpi->rc;
@@ -1872,12 +1923,13 @@
// Periodic key frames is based on the super-frame counter
// (svc.current_superframe), also only base spatial layer is key frame.
// Key frame is set for any of the following: very first frame, frame flags
- // indicates key, superframe counter hits key frequencey, or sync flag is
- // set for spatial layer 0.
- if ((cm->current_video_frame == 0) || (cpi->frame_flags & FRAMEFLAGS_KEY) ||
+ // indicates key, superframe counter hits key frequencey, or (non-intra) sync
+ // flag is set for spatial layer 0.
+ if ((cm->current_video_frame == 0 && !svc->previous_frame_is_intra_only) ||
+ (cpi->frame_flags & FRAMEFLAGS_KEY) ||
(cpi->oxcf.auto_key &&
(svc->current_superframe % cpi->oxcf.key_freq == 0) &&
- svc->spatial_layer_id == 0) ||
+ !svc->previous_frame_is_intra_only && svc->spatial_layer_id == 0) ||
(svc->spatial_layer_sync[0] == 1 && svc->spatial_layer_id == 0)) {
cm->frame_type = KEY_FRAME;
rc->source_alt_ref_active = 0;
@@ -1895,8 +1947,11 @@
cm->frame_type = INTER_FRAME;
if (is_one_pass_cbr_svc(cpi)) {
LAYER_CONTEXT *lc = &svc->layer_context[layer];
+ // Add condition current_video_frame > 0 for the case where first frame
+ // is intra only followed by overlay/copy frame. In this case we don't
+ // want to reset is_key_frame to 0 on overlay/copy frame.
lc->is_key_frame =
- svc->spatial_layer_id == 0
+ (svc->spatial_layer_id == 0 && cm->current_video_frame > 0)
? 0
: svc->layer_context[svc->temporal_layer_id].is_key_frame;
target = calc_pframe_target_size_one_pass_cbr(cpi);
@@ -1948,6 +2003,10 @@
rc->frames_till_gf_update_due = INT_MAX;
rc->baseline_gf_interval = INT_MAX;
}
+ if (svc->set_intra_only_frame) {
+ set_intra_only_frame(cpi);
+ target = calc_iframe_target_size_one_pass_cbr(cpi);
+ }
// Any update/change of global cyclic refresh parameters (amount/delta-qp)
// should be done here, before the frame qp is selected.
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ)
@@ -1961,8 +2020,8 @@
RATE_CONTROL *const rc = &cpi->rc;
int target;
// TODO(yaowu): replace the "auto_key && 0" below with proper decision logic.
- if ((cm->current_video_frame == 0 || (cpi->frame_flags & FRAMEFLAGS_KEY) ||
- rc->frames_to_key == 0 || (cpi->oxcf.auto_key && 0))) {
+ if ((cm->current_video_frame == 0) || (cpi->frame_flags & FRAMEFLAGS_KEY) ||
+ rc->frames_to_key == 0 || (cpi->oxcf.auto_key && 0)) {
cm->frame_type = KEY_FRAME;
rc->frames_to_key = cpi->oxcf.key_freq;
rc->kf_boost = DEFAULT_KF_BOOST;
@@ -1989,7 +2048,7 @@
if (cpi->oxcf.aq_mode == CYCLIC_REFRESH_AQ)
vp9_cyclic_refresh_update_parameters(cpi);
- if (cm->frame_type == KEY_FRAME)
+ if (frame_is_intra_only(cm))
target = calc_iframe_target_size_one_pass_cbr(cpi);
else
target = calc_pframe_target_size_one_pass_cbr(cpi);
--- a/vp9/encoder/vp9_speed_features.c
+++ b/vp9/encoder/vp9_speed_features.c
@@ -733,6 +733,10 @@
(uint8_t *)vpx_calloc((cm->mi_stride >> 3) * ((cm->mi_rows >> 3) + 1),
sizeof(*cpi->count_lastgolden_frame_usage));
}
+ if (cpi->svc.previous_frame_is_intra_only) {
+ sf->partition_search_type = FIXED_PARTITION;
+ sf->always_this_block_size = BLOCK_64X64;
+ }
}
void vp9_set_speed_features_framesize_dependent(VP9_COMP *cpi) {
--- a/vp9/encoder/vp9_svc_layercontext.c
+++ b/vp9/encoder/vp9_svc_layercontext.c
@@ -41,12 +41,14 @@
svc->skip_enhancement_layer = 0;
svc->disable_inter_layer_pred = INTER_LAYER_PRED_ON;
svc->framedrop_mode = CONSTRAINED_LAYER_DROP;
- svc->base_layer_intra_only = 0;
+ svc->set_intra_only_frame = 0;
+ svc->previous_frame_is_intra_only = 0;
svc->superframe_has_layer_sync = 0;
for (i = 0; i < REF_FRAMES; ++i) {
svc->fb_idx_spatial_layer_id[i] = -1;
svc->fb_idx_temporal_layer_id[i] = -1;
+ svc->fb_idx_base[i] = 0;
}
for (sl = 0; sl < oxcf->ss_number_layers; ++sl) {
svc->last_layer_dropped[sl] = 0;
@@ -1081,5 +1083,18 @@
cpi->ext_refresh_alt_ref_frame = 1;
}
}
+ }
+}
+
+void vp9_svc_update_ref_frame_buffer_idx(VP9_COMP *const cpi) {
+ SVC *const svc = &cpi->svc;
+ // Update the usage of frame buffer index for base spatial layers.
+ if (svc->spatial_layer_id == 0) {
+ if ((cpi->ref_frame_flags & VP9_LAST_FLAG) || cpi->refresh_last_frame)
+ svc->fb_idx_base[cpi->lst_fb_idx] = 1;
+ if ((cpi->ref_frame_flags & VP9_GOLD_FLAG) || cpi->refresh_golden_frame)
+ svc->fb_idx_base[cpi->gld_fb_idx] = 1;
+ if ((cpi->ref_frame_flags & VP9_ALT_FLAG) || cpi->refresh_alt_ref_frame)
+ svc->fb_idx_base[cpi->alt_fb_idx] = 1;
}
}
--- a/vp9/encoder/vp9_svc_layercontext.h
+++ b/vp9/encoder/vp9_svc_layercontext.h
@@ -160,8 +160,11 @@
uint8_t fb_idx_temporal_layer_id[REF_FRAMES];
int spatial_layer_sync[VPX_SS_MAX_LAYERS];
- int base_layer_intra_only;
+ uint8_t set_intra_only_frame;
+ uint8_t previous_frame_is_intra_only;
uint8_t superframe_has_layer_sync;
+
+ uint8_t fb_idx_base[REF_FRAMES];
} SVC;
struct VP9_COMP;
@@ -224,6 +227,8 @@
void vp9_svc_assert_constraints_pattern(struct VP9_COMP *const cpi);
void vp9_svc_check_spatial_layer_sync(struct VP9_COMP *const cpi);
+
+void vp9_svc_update_ref_frame_buffer_idx(struct VP9_COMP *const cpi);
#ifdef __cplusplus
} // extern "C"
--- a/vp9/vp9_cx_iface.c
+++ b/vp9/vp9_cx_iface.c
@@ -1553,9 +1553,7 @@
int sl;
for (sl = 0; sl < cpi->svc.number_spatial_layers; ++sl)
cpi->svc.spatial_layer_sync[sl] = data->spatial_layer_sync[sl];
- // TODO(marpan): Implement intra_only feature. Currently this setting
- // does nothing.
- cpi->svc.base_layer_intra_only = data->base_layer_intra_only;
+ cpi->svc.set_intra_only_frame = data->base_layer_intra_only;
return VPX_CODEC_OK;
}