diff --git a/vp9/encoder/vp9_firstpass.c b/vp9/encoder/vp9_firstpass.c index 61ca99637..857795167 100644 --- a/vp9/encoder/vp9_firstpass.c +++ b/vp9/encoder/vp9_firstpass.c @@ -257,12 +257,21 @@ static void avg_stats(FIRSTPASS_STATS *section) { // harder frames. static double calculate_modified_err(const VP9_COMP *cpi, const FIRSTPASS_STATS *this_frame) { - const struct twopass_rc *const twopass = &cpi->twopass; - const FIRSTPASS_STATS *const stats = &twopass->total_stats; - const double av_err = stats->ssim_weighted_pred_err / stats->count; - double modified_error = av_err * pow(this_frame->ssim_weighted_pred_err / - DOUBLE_DIVIDE_CHECK(av_err), - cpi->oxcf.two_pass_vbrbias / 100.0); + const struct twopass_rc *twopass = &cpi->twopass; + const FIRSTPASS_STATS *stats; + double av_err; + double modified_error; + + if (cpi->svc.number_spatial_layers > 1 && + cpi->svc.number_temporal_layers == 1) { + twopass = &cpi->svc.layer_context[cpi->svc.spatial_layer_id].twopass; + } + + stats = &twopass->total_stats; + av_err = stats->ssim_weighted_pred_err / stats->count; + modified_error = av_err * pow(this_frame->ssim_weighted_pred_err / + DOUBLE_DIVIDE_CHECK(av_err), + cpi->oxcf.two_pass_vbrbias / 100.0); return fclamp(modified_error, twopass->modified_error_min, twopass->modified_error_max); @@ -933,80 +942,108 @@ extern void vp9_new_framerate(VP9_COMP *cpi, double framerate); void vp9_init_second_pass(VP9_COMP *cpi) { FIRSTPASS_STATS this_frame; const FIRSTPASS_STATS *start_pos; - struct twopass_rc *const twopass = &cpi->twopass; + struct twopass_rc *twopass = &cpi->twopass; 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; - zero_stats(&twopass->total_stats); - zero_stats(&twopass->total_left_stats); - - if (!twopass->stats_in_end) - return; - - twopass->total_stats = *twopass->stats_in_end; - twopass->total_left_stats = twopass->total_stats; - - // 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. - vp9_new_framerate(cpi, 10000000.0 * twopass->total_stats.count / - twopass->total_stats.duration); - - cpi->output_framerate = oxcf->framerate; - twopass->bits_left = (int64_t)(twopass->total_stats.duration * - oxcf->target_bandwidth / 10000000.0); - - // 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; - - // 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); - } - - twopass->avg_iiratio = sum_iiratio / - DOUBLE_DIVIDE_CHECK((double)twopass->total_stats.count); - - reset_fpf_position(twopass, start_pos); + if (is_spatial_svc) { + layer_end = cpi->svc.number_spatial_layers; } - // 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); - - start_pos = twopass->stats_in; - - 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); + for (layer = 0; layer < layer_end; ++layer) { + if (is_spatial_svc) { + twopass = &cpi->svc.layer_context[layer].twopass; + cpi->svc.spatial_layer_id = layer; } - twopass->modified_error_left = twopass->modified_error_total; + 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; - reset_fpf_position(twopass, start_pos); + 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); + } + + cpi->output_framerate = oxcf->framerate; + + // 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; + + // 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); + } + + twopass->avg_iiratio = sum_iiratio / + DOUBLE_DIVIDE_CHECK((double)twopass->total_stats.count); + + reset_fpf_position(twopass, start_pos); + } + + // 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); + + start_pos = twopass->stats_in; + + 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 diff --git a/vp9/encoder/vp9_onyx_if.c b/vp9/encoder/vp9_onyx_if.c index e30f5346a..96bd23104 100644 --- a/vp9/encoder/vp9_onyx_if.c +++ b/vp9/encoder/vp9_onyx_if.c @@ -1228,8 +1228,9 @@ static void init_config(struct VP9_COMP *cpi, VP9_CONFIG *oxcf) { // Temporal scalability. cpi->svc.number_temporal_layers = oxcf->ts_number_layers; - 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_init_layer_context(cpi); } @@ -1387,8 +1388,9 @@ void vp9_change_config(struct VP9_COMP *cpi, const VP9_CONFIG *oxcf) { } update_frame_size(cpi); - 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_update_layer_context_change_config(cpi, (int)cpi->oxcf.target_bandwidth); } @@ -3527,7 +3529,7 @@ int vp9_get_compressed_data(VP9_COMP *cpi, unsigned int *frame_flags, if (cpi->svc.number_temporal_layers > 1 && cpi->oxcf.end_usage == USAGE_STREAM_FROM_SERVER) { - vp9_update_layer_framerate(cpi); + vp9_update_temporal_layer_framerate(cpi); vp9_restore_layer_context(cpi); } diff --git a/vp9/encoder/vp9_svc_layercontext.c b/vp9/encoder/vp9_svc_layercontext.c index 4ccc2bdc5..5d80d899e 100644 --- a/vp9/encoder/vp9_svc_layercontext.c +++ b/vp9/encoder/vp9_svc_layercontext.c @@ -16,10 +16,18 @@ void vp9_init_layer_context(VP9_COMP *const cpi) { const VP9_CONFIG *const oxcf = &cpi->oxcf; int layer; + int layer_end; cpi->svc.spatial_layer_id = 0; cpi->svc.temporal_layer_id = 0; - for (layer = 0; layer < cpi->svc.number_temporal_layers; ++layer) { + + if (cpi->svc.number_temporal_layers > 1) { + layer_end = cpi->svc.number_temporal_layers; + } else { + layer_end = cpi->svc.number_spatial_layers; + } + + for (layer = 0; layer < layer_end; ++layer) { LAYER_CONTEXT *const lc = &cpi->svc.layer_context[layer]; RATE_CONTROL *const lrc = &lc->rc; @@ -36,7 +44,13 @@ void vp9_init_layer_context(VP9_COMP *const cpi) { lrc->decimation_factor = 0; lrc->rate_correction_factor = 1.0; lrc->key_frame_rate_correction_factor = 1.0; - lc->target_bandwidth = oxcf->ts_target_bitrate[layer] * 1000; + + if (cpi->svc.number_temporal_layers > 1) { + lc->target_bandwidth = oxcf->ts_target_bitrate[layer] * 1000; + } else { + lc->target_bandwidth = oxcf->ss_target_bitrate[layer] * 1000; + } + lrc->buffer_level = vp9_rescale((int)(oxcf->starting_buffer_level), lc->target_bandwidth, 1000); lrc->bits_off_target = lrc->buffer_level; @@ -49,12 +63,24 @@ void vp9_update_layer_context_change_config(VP9_COMP *const cpi, const VP9_CONFIG *const oxcf = &cpi->oxcf; const RATE_CONTROL *const rc = &cpi->rc; int layer; + int layer_end; float bitrate_alloc = 1.0; - for (layer = 0; layer < cpi->svc.number_temporal_layers; ++layer) { + if (cpi->svc.number_temporal_layers > 1) { + layer_end = cpi->svc.number_temporal_layers; + } else { + layer_end = cpi->svc.number_spatial_layers; + } + + for (layer = 0; layer < layer_end; ++layer) { LAYER_CONTEXT *const lc = &cpi->svc.layer_context[layer]; RATE_CONTROL *const lrc = &lc->rc; - lc->target_bandwidth = oxcf->ts_target_bitrate[layer] * 1000; + + if (cpi->svc.number_temporal_layers > 1) { + lc->target_bandwidth = oxcf->ts_target_bitrate[layer] * 1000; + } else { + lc->target_bandwidth = oxcf->ss_target_bitrate[layer] * 1000; + } bitrate_alloc = (float)lc->target_bandwidth / target_bandwidth; // Update buffer-related quantities. lc->starting_buffer_level = @@ -66,7 +92,11 @@ void vp9_update_layer_context_change_config(VP9_COMP *const cpi, lrc->bits_off_target = MIN(lrc->bits_off_target, lc->maximum_buffer_size); lrc->buffer_level = MIN(lrc->buffer_level, lc->maximum_buffer_size); // Update framerate-related quantities. - lc->framerate = oxcf->framerate / oxcf->ts_rate_decimator[layer]; + if (cpi->svc.number_temporal_layers > 1) { + lc->framerate = oxcf->framerate / oxcf->ts_rate_decimator[layer]; + } else { + lc->framerate = oxcf->framerate; + } lrc->av_per_frame_bandwidth = (int)(lc->target_bandwidth / lc->framerate); lrc->max_frame_bandwidth = rc->max_frame_bandwidth; // Update qp-related quantities. @@ -79,7 +109,7 @@ static LAYER_CONTEXT *get_temporal_layer_context(SVC *svc) { return &svc->layer_context[svc->temporal_layer_id]; } -void vp9_update_layer_framerate(VP9_COMP *const cpi) { +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); @@ -102,6 +132,21 @@ void vp9_update_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]; + RATE_CONTROL *const lrc = &lc->rc; + + lc->framerate = framerate; + lrc->av_per_frame_bandwidth = (int)(lc->target_bandwidth / lc->framerate); + lrc->min_frame_bandwidth = (int)(lrc->av_per_frame_bandwidth * + oxcf->two_pass_vbrmin_section / 100); + lrc->max_frame_bandwidth = (int)(((int64_t)lrc->av_per_frame_bandwidth * + oxcf->two_pass_vbrmax_section) / 100); + lrc->max_gf_interval = 16; +} + void vp9_restore_layer_context(VP9_COMP *const cpi) { LAYER_CONTEXT *const lc = get_temporal_layer_context(&cpi->svc); const int old_frame_since_key = cpi->rc.frames_since_key; diff --git a/vp9/encoder/vp9_svc_layercontext.h b/vp9/encoder/vp9_svc_layercontext.h index afbbdf086..2cd4e479c 100644 --- a/vp9/encoder/vp9_svc_layercontext.h +++ b/vp9/encoder/vp9_svc_layercontext.h @@ -52,8 +52,12 @@ void vp9_update_layer_context_change_config(struct VP9_COMP *const cpi, const int target_bandwidth); // Prior to encoding the frame, update framerate-related quantities -// for the current layer. -void vp9_update_layer_framerate(struct VP9_COMP *const cpi); +// for the current temporal layer. +void vp9_update_temporal_layer_framerate(struct VP9_COMP *const cpi); + +// Update framerate-related quantities for the current spatial layer. +void vp9_update_spatial_layer_framerate(struct VP9_COMP *const cpi, + double framerate); // Prior to encoding the frame, set the layer context, for the current layer // to be encoded, to the cpi struct.