From 9ee6087f34db386ae40cdf4bc5e41120cb8075a6 Mon Sep 17 00:00:00 2001 From: Minghai Shang Date: Thu, 27 Mar 2014 16:59:44 -0700 Subject: [PATCH] [svc] Finalize first version of 2nd pass rc Change-Id: I366850015004644c4fc7feabe27a782fdd8d8718 --- test/svc_test.cc | 45 +++++++++- vp9/encoder/vp9_firstpass.c | 180 ++++++++++++++++++++----------------- vp9/encoder/vp9_onyx_if.c | 30 +++++-- vp9/encoder/vp9_ratectrl.c | 2 +- vp9/encoder/vp9_svc_layercontext.c | 57 +++++++++--- vp9/encoder/vp9_svc_layercontext.h | 8 +- 6 files changed, 218 insertions(+), 104 deletions(-) diff --git a/test/svc_test.cc b/test/svc_test.cc index 30508df..fb9277b 100644 --- a/test/svc_test.cc +++ b/test/svc_test.cc @@ -419,9 +419,52 @@ TEST_F(SvcTest, SecondPassEncode) { fclose(stats_file); codec_enc_.rc_twopass_stats_in = stats_buf; - const vpx_codec_err_t res = + vpx_codec_err_t res = vpx_svc_init(&svc_, &codec_, vpx_codec_vp9_cx(), &codec_enc_); ASSERT_EQ(VPX_CODEC_OK, res); + codec_initialized_ = true; + + libvpx_test::I420VideoSource video(test_file_name_, kWidth, kHeight, + codec_enc_.g_timebase.den, + codec_enc_.g_timebase.num, 0, 30); + // FRAME 0 + video.Begin(); + // This frame is a keyframe. + res = vpx_svc_encode(&svc_, &codec_, video.img(), video.pts(), + video.duration(), VPX_DL_GOOD_QUALITY); + ASSERT_EQ(VPX_CODEC_OK, res); + EXPECT_EQ(1, vpx_svc_is_keyframe(&svc_)); + + vpx_codec_err_t res_dec = decoder_->DecodeFrame( + static_cast(vpx_svc_get_buffer(&svc_)), + vpx_svc_get_frame_size(&svc_)); + ASSERT_EQ(VPX_CODEC_OK, res_dec) << decoder_->DecodeError(); + + // FRAME 1 + video.Next(); + // This is a P-frame. + res = vpx_svc_encode(&svc_, &codec_, video.img(), video.pts(), + video.duration(), VPX_DL_GOOD_QUALITY); + ASSERT_EQ(VPX_CODEC_OK, res); + EXPECT_EQ(0, vpx_svc_is_keyframe(&svc_)); + + res_dec = decoder_->DecodeFrame( + static_cast(vpx_svc_get_buffer(&svc_)), + vpx_svc_get_frame_size(&svc_)); + ASSERT_EQ(VPX_CODEC_OK, res_dec) << decoder_->DecodeError(); + + // FRAME 2 + video.Next(); + // This is a P-frame. + res = vpx_svc_encode(&svc_, &codec_, video.img(), video.pts(), + video.duration(), VPX_DL_GOOD_QUALITY); + ASSERT_EQ(VPX_CODEC_OK, res); + EXPECT_EQ(0, vpx_svc_is_keyframe(&svc_)); + + res_dec = decoder_->DecodeFrame( + static_cast(vpx_svc_get_buffer(&svc_)), + vpx_svc_get_frame_size(&svc_)); + ASSERT_EQ(VPX_CODEC_OK, res_dec) << decoder_->DecodeError(); free(stats_buf.buf); } diff --git a/vp9/encoder/vp9_firstpass.c b/vp9/encoder/vp9_firstpass.c index 8577951..6d36b7c 100644 --- a/vp9/encoder/vp9_firstpass.c +++ b/vp9/encoder/vp9_firstpass.c @@ -946,104 +946,97 @@ void vp9_init_second_pass(VP9_COMP *cpi) { const VP9_CONFIG *const oxcf = &cpi->oxcf; const int is_spatial_svc = (cpi->svc.number_spatial_layers > 1) && (cpi->svc.number_temporal_layers == 1); - int layer = 0; - int layer_end = 1; double frame_rate; if (is_spatial_svc) { - layer_end = cpi->svc.number_spatial_layers; + twopass = &cpi->svc.layer_context[cpi->svc.spatial_layer_id].twopass; } - for (layer = 0; layer < layer_end; ++layer) { - if (is_spatial_svc) { - twopass = &cpi->svc.layer_context[layer].twopass; - cpi->svc.spatial_layer_id = layer; - } - zero_stats(&twopass->total_stats); - zero_stats(&twopass->total_left_stats); - twopass->total_stats.spatial_layer_id = layer; - twopass->total_left_stats.spatial_layer_id = layer; - - if (!twopass->stats_in_end) - continue; - - twopass->total_stats = *twopass->stats_in_end; - twopass->total_left_stats = twopass->total_stats; - - frame_rate = 10000000.0 * twopass->total_stats.count / - twopass->total_stats.duration; - // Each frame can have a different duration, as the frame rate in the source - // isn't guaranteed to be constant. The frame rate prior to the first frame - // encoded in the second pass is a guess. However, the sum duration is not. - // It is calculated based on the actual durations of all frames from the - // first pass. - if (layer == 0) { - vp9_new_framerate(cpi, frame_rate); - } - if (is_spatial_svc) { - vp9_update_spatial_layer_framerate(cpi, frame_rate); - twopass->bits_left = (int64_t)(twopass->total_stats.duration * - cpi->svc.layer_context[layer].target_bandwidth / - 10000000.0); - } else { - twopass->bits_left = (int64_t)(twopass->total_stats.duration * - oxcf->target_bandwidth / 10000000.0); - } + zero_stats(&twopass->total_stats); + zero_stats(&twopass->total_left_stats); - cpi->output_framerate = oxcf->framerate; + if (!twopass->stats_in_end) + return; - // Calculate a minimum intra value to be used in determining the IIratio - // scores used in the second pass. We have this minimum to make sure - // that clips that are static but "low complexity" in the intra domain - // are still boosted appropriately for KF/GF/ARF. - twopass->kf_intra_err_min = KF_MB_INTRA_MIN * cpi->common.MBs; - twopass->gf_intra_err_min = GF_MB_INTRA_MIN * cpi->common.MBs; + twopass->total_stats = *twopass->stats_in_end; + twopass->total_left_stats = twopass->total_stats; - // This variable monitors how far behind the second ref update is lagging. - twopass->sr_update_lag = 1; + frame_rate = 10000000.0 * twopass->total_stats.count / + twopass->total_stats.duration; + // Each frame can have a different duration, as the frame rate in the source + // isn't guaranteed to be constant. The frame rate prior to the first frame + // encoded in the second pass is a guess. However, the sum duration is not. + // It is calculated based on the actual durations of all frames from the + // first pass. - // Scan the first pass file and calculate an average Intra / Inter error - // score ratio for the sequence. - { - double sum_iiratio = 0.0; - start_pos = twopass->stats_in; + if (is_spatial_svc) { + vp9_update_spatial_layer_framerate(cpi, frame_rate); + twopass->bits_left = + (int64_t)(twopass->total_stats.duration * + cpi->svc.layer_context[cpi->svc.spatial_layer_id].target_bandwidth / + 10000000.0); + } else { + vp9_new_framerate(cpi, frame_rate); + twopass->bits_left = (int64_t)(twopass->total_stats.duration * + oxcf->target_bandwidth / 10000000.0); + } - while (input_stats(twopass, &this_frame) != EOF) { - const double iiratio = this_frame.intra_error / - DOUBLE_DIVIDE_CHECK(this_frame.coded_error); - sum_iiratio += fclamp(iiratio, 1.0, 20.0); - } + cpi->output_framerate = oxcf->framerate; - twopass->avg_iiratio = sum_iiratio / - DOUBLE_DIVIDE_CHECK((double)twopass->total_stats.count); + // Calculate a minimum intra value to be used in determining the IIratio + // scores used in the second pass. We have this minimum to make sure + // that clips that are static but "low complexity" in the intra domain + // are still boosted appropriately for KF/GF/ARF. + if (!is_spatial_svc) { + // We don't know the number of MBs for each layer at this point. + // So we will do it later. + twopass->kf_intra_err_min = KF_MB_INTRA_MIN * cpi->common.MBs; + twopass->gf_intra_err_min = GF_MB_INTRA_MIN * cpi->common.MBs; + } - reset_fpf_position(twopass, start_pos); + // This variable monitors how far behind the second ref update is lagging. + twopass->sr_update_lag = 1; + + // Scan the first pass file and calculate an average Intra / Inter error + // score ratio for the sequence. + { + double sum_iiratio = 0.0; + start_pos = twopass->stats_in; + + while (input_stats(twopass, &this_frame) != EOF) { + const double iiratio = this_frame.intra_error / + DOUBLE_DIVIDE_CHECK(this_frame.coded_error); + sum_iiratio += fclamp(iiratio, 1.0, 20.0); } - // Scan the first pass file and calculate a modified total error based upon - // the bias/power function used to allocate bits. - { - double av_error = twopass->total_stats.ssim_weighted_pred_err / - DOUBLE_DIVIDE_CHECK(twopass->total_stats.count); + twopass->avg_iiratio = sum_iiratio / + DOUBLE_DIVIDE_CHECK((double)twopass->total_stats.count); - start_pos = twopass->stats_in; + reset_fpf_position(twopass, start_pos); + } - twopass->modified_error_total = 0.0; - twopass->modified_error_min = - (av_error * oxcf->two_pass_vbrmin_section) / 100; - twopass->modified_error_max = - (av_error * oxcf->two_pass_vbrmax_section) / 100; + // Scan the first pass file and calculate a modified total error based upon + // the bias/power function used to allocate bits. + { + double av_error = twopass->total_stats.ssim_weighted_pred_err / + DOUBLE_DIVIDE_CHECK(twopass->total_stats.count); - while (input_stats(twopass, &this_frame) != EOF) { - twopass->modified_error_total += - calculate_modified_err(cpi, &this_frame); - } - twopass->modified_error_left = twopass->modified_error_total; + start_pos = twopass->stats_in; - reset_fpf_position(twopass, start_pos); + twopass->modified_error_total = 0.0; + twopass->modified_error_min = + (av_error * oxcf->two_pass_vbrmin_section) / 100; + twopass->modified_error_max = + (av_error * oxcf->two_pass_vbrmax_section) / 100; + + while (input_stats(twopass, &this_frame) != EOF) { + twopass->modified_error_total += + calculate_modified_err(cpi, &this_frame); } + twopass->modified_error_left = twopass->modified_error_total; + + reset_fpf_position(twopass, start_pos); } - cpi->svc.spatial_layer_id = 0; } // This function gives an estimate of how badly we believe the prediction @@ -1652,8 +1645,8 @@ static void define_gf_group(VP9_COMP *cpi, FIRSTPASS_STATS *this_frame) { // Calculate the bits to be allocated to the group as a whole. if (twopass->kf_group_bits > 0 && twopass->kf_group_error_left > 0) { - twopass->gf_group_bits = (int64_t)(cpi->twopass.kf_group_bits * - (gf_group_err / cpi->twopass.kf_group_error_left)); + twopass->gf_group_bits = (int64_t)(twopass->kf_group_bits * + (gf_group_err / twopass->kf_group_error_left)); } else { twopass->gf_group_bits = 0; } @@ -2224,14 +2217,24 @@ void vp9_rc_get_second_pass_params(VP9_COMP *cpi) { VP9_COMMON *const cm = &cpi->common; RATE_CONTROL *const rc = &cpi->rc; struct twopass_rc *const twopass = &cpi->twopass; - const int frames_left = (int)(twopass->total_stats.count - - cm->current_video_frame); + int frames_left; FIRSTPASS_STATS this_frame; FIRSTPASS_STATS this_frame_copy; double this_frame_intra_error; double this_frame_coded_error; int target; + LAYER_CONTEXT *lc = NULL; + int is_spatial_svc = (cpi->use_svc && cpi->svc.number_temporal_layers == 1); + + if (is_spatial_svc) { + lc = &cpi->svc.layer_context[cpi->svc.spatial_layer_id]; + frames_left = (int)(twopass->total_stats.count - + lc->current_video_frame_in_layer); + } else { + frames_left = (int)(twopass->total_stats.count - + cm->current_video_frame); + } if (!twopass->stats_in) return; @@ -2244,9 +2247,15 @@ void vp9_rc_get_second_pass_params(VP9_COMP *cpi) { vp9_clear_system_state(); + if (is_spatial_svc && twopass->kf_intra_err_min == 0) { + twopass->kf_intra_err_min = KF_MB_INTRA_MIN * cpi->common.MBs; + twopass->gf_intra_err_min = GF_MB_INTRA_MIN * cpi->common.MBs; + } + if (cpi->oxcf.end_usage == USAGE_CONSTANT_QUALITY) { twopass->active_worst_quality = cpi->oxcf.cq_level; - } else if (cm->current_video_frame == 0) { + } else if (cm->current_video_frame == 0 || + (is_spatial_svc && lc->current_video_frame_in_layer == 0)) { // Special case code for first frame. const int section_target_bandwidth = (int)(twopass->bits_left / frames_left); @@ -2269,6 +2278,11 @@ void vp9_rc_get_second_pass_params(VP9_COMP *cpi) { // Define next KF group and assign bits to it. this_frame_copy = this_frame; find_next_key_frame(cpi, &this_frame_copy); + // Don't place key frame in any enhancement layers in spatial svc + if (cpi->use_svc && cpi->svc.number_temporal_layers == 1 && + cpi->svc.spatial_layer_id > 0) { + cm->frame_type = INTER_FRAME; + } } else { cm->frame_type = INTER_FRAME; } diff --git a/vp9/encoder/vp9_onyx_if.c b/vp9/encoder/vp9_onyx_if.c index 96bd231..e5958db 100644 --- a/vp9/encoder/vp9_onyx_if.c +++ b/vp9/encoder/vp9_onyx_if.c @@ -1230,7 +1230,8 @@ static void init_config(struct VP9_COMP *cpi, VP9_CONFIG *oxcf) { if ((cpi->svc.number_temporal_layers > 1 && cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) || - (cpi->svc.number_spatial_layers > 1 && cpi->pass == 2)) { + (cpi->svc.number_spatial_layers > 1 && + cpi->oxcf.mode == MODE_SECONDPASS_BEST)) { vp9_init_layer_context(cpi); } @@ -1795,13 +1796,15 @@ VP9_COMP *vp9_create_compressor(VP9_CONFIG *oxcf) { ++stats_copy[layer_id]; } } + + vp9_init_second_pass_spatial_svc(cpi); } else { cpi->twopass.stats_in_start = oxcf->two_pass_stats_in.buf; cpi->twopass.stats_in = cpi->twopass.stats_in_start; cpi->twopass.stats_in_end = &cpi->twopass.stats_in[packets - 1]; - } - vp9_init_second_pass(cpi); + vp9_init_second_pass(cpi); + } } vp9_set_speed_features(cpi); @@ -3242,6 +3245,15 @@ static void encode_frame_to_data_rate(VP9_COMP *cpi, // Don't increment frame counters if this was an altref buffer // update not a real frame ++cm->current_video_frame; + if (cpi->use_svc) { + LAYER_CONTEXT *lc; + if (cpi->svc.number_temporal_layers > 1) { + lc = &cpi->svc.layer_context[cpi->svc.temporal_layer_id]; + } else { + lc = &cpi->svc.layer_context[cpi->svc.spatial_layer_id]; + } + ++lc->current_video_frame_in_layer; + } } // restore prev_mi @@ -3397,6 +3409,10 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, if (!cpi) return -1; + if (cpi->svc.number_spatial_layers > 1 && cpi->pass == 2) { + vp9_restore_layer_context(cpi); + } + vpx_usec_timer_start(&cmptimer); cpi->source = NULL; @@ -3589,7 +3605,8 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, if (cpi->pass == 1 && (!cpi->use_svc || cpi->svc.number_temporal_layers == 1)) { Pass1Encode(cpi, size, dest, frame_flags); - } else if (cpi->pass == 2 && !cpi->use_svc) { + } else if (cpi->pass == 2 && + (!cpi->use_svc || cpi->svc.number_temporal_layers == 1)) { Pass2Encode(cpi, size, dest, frame_flags); } else if (cpi->use_svc) { SvcEncode(cpi, size, dest, frame_flags); @@ -3611,8 +3628,9 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, } // Save layer specific state. - if (cpi->svc.number_temporal_layers > 1 && - cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) { + if ((cpi->svc.number_temporal_layers > 1 && + cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) || + (cpi->svc.number_spatial_layers > 1 && cpi->pass == 2)) { vp9_save_layer_context(cpi); } diff --git a/vp9/encoder/vp9_ratectrl.c b/vp9/encoder/vp9_ratectrl.c index dd8a641..c62d312 100644 --- a/vp9/encoder/vp9_ratectrl.c +++ b/vp9/encoder/vp9_ratectrl.c @@ -933,7 +933,7 @@ static int rc_pick_q_and_bounds_two_pass(const VP9_COMP *cpi, active_best_quality, active_worst_quality); if (q > *top_index) { // Special case when we are targeting the max allowed rate. - if (cpi->rc.this_frame_target >= cpi->rc.max_frame_bandwidth) + if (rc->this_frame_target >= rc->max_frame_bandwidth) *top_index = q; else q = *top_index; diff --git a/vp9/encoder/vp9_svc_layercontext.c b/vp9/encoder/vp9_svc_layercontext.c index 5d80d89..2b5639d 100644 --- a/vp9/encoder/vp9_svc_layercontext.c +++ b/vp9/encoder/vp9_svc_layercontext.c @@ -30,9 +30,8 @@ void vp9_init_layer_context(VP9_COMP *const cpi) { for (layer = 0; layer < layer_end; ++layer) { LAYER_CONTEXT *const lc = &cpi->svc.layer_context[layer]; RATE_CONTROL *const lrc = &lc->rc; - + lc->current_video_frame_in_layer = 0; lrc->avg_frame_qindex[INTER_FRAME] = q_trans[oxcf->worst_allowed_q]; - lrc->last_q[INTER_FRAME] = q_trans[oxcf->worst_allowed_q]; lrc->ni_av_qi = q_trans[oxcf->worst_allowed_q]; lrc->total_actual_bits = 0; lrc->total_target_vs_actual = 0; @@ -47,8 +46,12 @@ void vp9_init_layer_context(VP9_COMP *const cpi) { if (cpi->svc.number_temporal_layers > 1) { lc->target_bandwidth = oxcf->ts_target_bitrate[layer] * 1000; + lrc->last_q[INTER_FRAME] = q_trans[oxcf->worst_allowed_q]; } else { lc->target_bandwidth = oxcf->ss_target_bitrate[layer] * 1000; + lrc->last_q[0] = q_trans[oxcf->best_allowed_q]; + lrc->last_q[1] = q_trans[oxcf->best_allowed_q]; + lrc->last_q[2] = q_trans[oxcf->best_allowed_q]; } lrc->buffer_level = vp9_rescale((int)(oxcf->starting_buffer_level), @@ -105,14 +108,16 @@ void vp9_update_layer_context_change_config(VP9_COMP *const cpi, } } -static LAYER_CONTEXT *get_temporal_layer_context(SVC *svc) { - return &svc->layer_context[svc->temporal_layer_id]; +static LAYER_CONTEXT *get_layer_context(SVC *svc) { + return svc->number_temporal_layers > 1 ? + &svc->layer_context[svc->temporal_layer_id] : + &svc->layer_context[svc->spatial_layer_id]; } void vp9_update_temporal_layer_framerate(VP9_COMP *const cpi) { const int layer = cpi->svc.temporal_layer_id; const VP9_CONFIG *const oxcf = &cpi->oxcf; - LAYER_CONTEXT *const lc = get_temporal_layer_context(&cpi->svc); + LAYER_CONTEXT *const lc = get_layer_context(&cpi->svc); RATE_CONTROL *const lrc = &lc->rc; lc->framerate = oxcf->framerate / oxcf->ts_rate_decimator[layer]; @@ -133,9 +138,8 @@ void vp9_update_temporal_layer_framerate(VP9_COMP *const cpi) { } void vp9_update_spatial_layer_framerate(VP9_COMP *const cpi, double framerate) { - int layer = cpi->svc.spatial_layer_id; const VP9_CONFIG *const oxcf = &cpi->oxcf; - LAYER_CONTEXT *const lc = &cpi->svc.layer_context[layer]; + LAYER_CONTEXT *const lc = get_layer_context(&cpi->svc); RATE_CONTROL *const lrc = &lc->rc; lc->framerate = framerate; @@ -145,14 +149,28 @@ void vp9_update_spatial_layer_framerate(VP9_COMP *const cpi, double framerate) { lrc->max_frame_bandwidth = (int)(((int64_t)lrc->av_per_frame_bandwidth * oxcf->two_pass_vbrmax_section) / 100); lrc->max_gf_interval = 16; + + lrc->static_scene_max_gf_interval = cpi->key_frame_frequency >> 1; + + if (oxcf->play_alternate && oxcf->lag_in_frames) { + if (lrc->max_gf_interval > oxcf->lag_in_frames - 1) + lrc->max_gf_interval = oxcf->lag_in_frames - 1; + + if (lrc->static_scene_max_gf_interval > oxcf->lag_in_frames - 1) + lrc->static_scene_max_gf_interval = oxcf->lag_in_frames - 1; + } + + if (lrc->max_gf_interval > lrc->static_scene_max_gf_interval) + lrc->max_gf_interval = lrc->static_scene_max_gf_interval; } void vp9_restore_layer_context(VP9_COMP *const cpi) { - LAYER_CONTEXT *const lc = get_temporal_layer_context(&cpi->svc); + LAYER_CONTEXT *const lc = get_layer_context(&cpi->svc); const int old_frame_since_key = cpi->rc.frames_since_key; const int old_frame_to_key = cpi->rc.frames_to_key; cpi->rc = lc->rc; + cpi->twopass = lc->twopass; cpi->oxcf.target_bandwidth = lc->target_bandwidth; cpi->oxcf.starting_buffer_level = lc->starting_buffer_level; cpi->oxcf.optimal_buffer_level = lc->optimal_buffer_level; @@ -160,18 +178,35 @@ void vp9_restore_layer_context(VP9_COMP *const cpi) { cpi->output_framerate = lc->framerate; // Reset the frames_since_key and frames_to_key counters to their values // before the layer restore. Keep these defined for the stream (not layer). - cpi->rc.frames_since_key = old_frame_since_key; - cpi->rc.frames_to_key = old_frame_to_key; + if (cpi->svc.number_temporal_layers > 1) { + cpi->rc.frames_since_key = old_frame_since_key; + cpi->rc.frames_to_key = old_frame_to_key; + } } void vp9_save_layer_context(VP9_COMP *const cpi) { const VP9_CONFIG *const oxcf = &cpi->oxcf; - LAYER_CONTEXT *const lc = get_temporal_layer_context(&cpi->svc); + LAYER_CONTEXT *const lc = get_layer_context(&cpi->svc); lc->rc = cpi->rc; + lc->twopass = cpi->twopass; lc->target_bandwidth = (int)oxcf->target_bandwidth; lc->starting_buffer_level = oxcf->starting_buffer_level; lc->optimal_buffer_level = oxcf->optimal_buffer_level; lc->maximum_buffer_size = oxcf->maximum_buffer_size; lc->framerate = cpi->output_framerate; } + +void vp9_init_second_pass_spatial_svc(VP9_COMP *cpi) { + int i; + for (i = 0; i < cpi->svc.number_spatial_layers; ++i) { + struct twopass_rc *const twopass = &cpi->svc.layer_context[i].twopass; + + cpi->svc.spatial_layer_id = i; + vp9_init_second_pass(cpi); + + twopass->total_stats.spatial_layer_id = i; + twopass->total_left_stats.spatial_layer_id = i; + } + cpi->svc.spatial_layer_id = 0; +} diff --git a/vp9/encoder/vp9_svc_layercontext.h b/vp9/encoder/vp9_svc_layercontext.h index 2cd4e47..e859a2f 100644 --- a/vp9/encoder/vp9_svc_layercontext.h +++ b/vp9/encoder/vp9_svc_layercontext.h @@ -29,6 +29,7 @@ typedef struct { int avg_frame_size; struct twopass_rc twopass; struct vpx_fixed_buf rc_twopass_stats_in; + unsigned int current_video_frame_in_layer; } LAYER_CONTEXT; typedef struct { @@ -36,8 +37,8 @@ typedef struct { int temporal_layer_id; int number_spatial_layers; int number_temporal_layers; - // Layer context used for rate control in temporal CBR mode or spatial - // two pass mode. Defined for temporal or spatial layers for now. + // Layer context used for rate control in one pass temporal CBR mode or + // two pass spatial mode. Defined for temporal or spatial layers for now. // Does not support temporal combined with spatial RC. LAYER_CONTEXT layer_context[MAX(VPX_TS_MAX_LAYERS, VPX_SS_MAX_LAYERS)]; } SVC; @@ -66,6 +67,9 @@ void vp9_restore_layer_context(struct VP9_COMP *const cpi); // Save the layer context after encoding the frame. void vp9_save_layer_context(struct VP9_COMP *const cpi); +// Initialize second pass rc for spatial svc. +void vp9_init_second_pass_spatial_svc(struct VP9_COMP *cpi); + #ifdef __cplusplus } // extern "C" #endif -- 2.7.4