Merge changes I00c3e823,If8bca004
* changes: Improved key frame detection. Improved KF insertion after fades to still.
This commit is contained in:
@@ -312,7 +312,9 @@ void vp8_output_stats(const VP8_COMP *cpi,
|
|||||||
FILE *fpfile;
|
FILE *fpfile;
|
||||||
fpfile = fopen("firstpass.stt", "a");
|
fpfile = fopen("firstpass.stt", "a");
|
||||||
|
|
||||||
fprintf(fpfile, "%12.0f %12.0f %12.0f %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f %12.0f\n",
|
fprintf(fpfile, "%12.0f %12.0f %12.0f %12.4f %12.4f %12.4f %12.4f
|
||||||
|
%12.4f %12.4f %12.4f %12.4f %12.4f %12.4f %12.4f %12.0f
|
||||||
|
%12.4f\n",
|
||||||
stats->frame,
|
stats->frame,
|
||||||
stats->intra_error,
|
stats->intra_error,
|
||||||
stats->coded_error,
|
stats->coded_error,
|
||||||
@@ -320,6 +322,7 @@ void vp8_output_stats(const VP8_COMP *cpi,
|
|||||||
stats->pcnt_inter,
|
stats->pcnt_inter,
|
||||||
stats->pcnt_motion,
|
stats->pcnt_motion,
|
||||||
stats->pcnt_second_ref,
|
stats->pcnt_second_ref,
|
||||||
|
stats->pcnt_neutral,
|
||||||
stats->MVr,
|
stats->MVr,
|
||||||
stats->mvr_abs,
|
stats->mvr_abs,
|
||||||
stats->MVc,
|
stats->MVc,
|
||||||
@@ -327,7 +330,8 @@ void vp8_output_stats(const VP8_COMP *cpi,
|
|||||||
stats->MVrv,
|
stats->MVrv,
|
||||||
stats->MVcv,
|
stats->MVcv,
|
||||||
stats->mv_in_out_count,
|
stats->mv_in_out_count,
|
||||||
stats->count);
|
stats->count,
|
||||||
|
stats->duration);
|
||||||
fclose(fpfile);
|
fclose(fpfile);
|
||||||
|
|
||||||
|
|
||||||
@@ -359,6 +363,7 @@ void vp8_zero_stats(FIRSTPASS_STATS *section)
|
|||||||
section->pcnt_inter = 0.0;
|
section->pcnt_inter = 0.0;
|
||||||
section->pcnt_motion = 0.0;
|
section->pcnt_motion = 0.0;
|
||||||
section->pcnt_second_ref = 0.0;
|
section->pcnt_second_ref = 0.0;
|
||||||
|
section->pcnt_neutral = 0.0;
|
||||||
section->MVr = 0.0;
|
section->MVr = 0.0;
|
||||||
section->mvr_abs = 0.0;
|
section->mvr_abs = 0.0;
|
||||||
section->MVc = 0.0;
|
section->MVc = 0.0;
|
||||||
@@ -378,6 +383,7 @@ void vp8_accumulate_stats(FIRSTPASS_STATS *section, FIRSTPASS_STATS *frame)
|
|||||||
section->pcnt_inter += frame->pcnt_inter;
|
section->pcnt_inter += frame->pcnt_inter;
|
||||||
section->pcnt_motion += frame->pcnt_motion;
|
section->pcnt_motion += frame->pcnt_motion;
|
||||||
section->pcnt_second_ref += frame->pcnt_second_ref;
|
section->pcnt_second_ref += frame->pcnt_second_ref;
|
||||||
|
section->pcnt_neutral += frame->pcnt_neutral;
|
||||||
section->MVr += frame->MVr;
|
section->MVr += frame->MVr;
|
||||||
section->mvr_abs += frame->mvr_abs;
|
section->mvr_abs += frame->mvr_abs;
|
||||||
section->MVc += frame->MVc;
|
section->MVc += frame->MVc;
|
||||||
@@ -398,6 +404,7 @@ void vp8_avg_stats(FIRSTPASS_STATS *section)
|
|||||||
section->ssim_weighted_pred_err /= section->count;
|
section->ssim_weighted_pred_err /= section->count;
|
||||||
section->pcnt_inter /= section->count;
|
section->pcnt_inter /= section->count;
|
||||||
section->pcnt_second_ref /= section->count;
|
section->pcnt_second_ref /= section->count;
|
||||||
|
section->pcnt_neutral /= section->count;
|
||||||
section->pcnt_motion /= section->count;
|
section->pcnt_motion /= section->count;
|
||||||
section->MVr /= section->count;
|
section->MVr /= section->count;
|
||||||
section->mvr_abs /= section->count;
|
section->mvr_abs /= section->count;
|
||||||
@@ -570,6 +577,7 @@ void vp8_first_pass(VP8_COMP *cpi)
|
|||||||
int intercount = 0;
|
int intercount = 0;
|
||||||
int second_ref_count = 0;
|
int second_ref_count = 0;
|
||||||
int intrapenalty = 256;
|
int intrapenalty = 256;
|
||||||
|
int neutral_count = 0;
|
||||||
|
|
||||||
int sum_in_vectors = 0;
|
int sum_in_vectors = 0;
|
||||||
|
|
||||||
@@ -726,6 +734,17 @@ void vp8_first_pass(VP8_COMP *cpi)
|
|||||||
|
|
||||||
if (motion_error <= this_error)
|
if (motion_error <= this_error)
|
||||||
{
|
{
|
||||||
|
// Keep a count of cases where the inter and intra were
|
||||||
|
// very close and very low. This helps with scene cut
|
||||||
|
// detection for example in cropped clips with black bars
|
||||||
|
// at the sides or top and bottom.
|
||||||
|
if( (((this_error-intrapenalty) * 9) <=
|
||||||
|
(motion_error*10)) &&
|
||||||
|
(this_error < (2*intrapenalty)) )
|
||||||
|
{
|
||||||
|
neutral_count++;
|
||||||
|
}
|
||||||
|
|
||||||
d->bmi.mv.as_mv.row <<= 3;
|
d->bmi.mv.as_mv.row <<= 3;
|
||||||
d->bmi.mv.as_mv.col <<= 3;
|
d->bmi.mv.as_mv.col <<= 3;
|
||||||
this_error = motion_error;
|
this_error = motion_error;
|
||||||
@@ -854,6 +873,7 @@ void vp8_first_pass(VP8_COMP *cpi)
|
|||||||
|
|
||||||
fps.pcnt_inter = 1.0 * (double)intercount / cm->MBs;
|
fps.pcnt_inter = 1.0 * (double)intercount / cm->MBs;
|
||||||
fps.pcnt_second_ref = 1.0 * (double)second_ref_count / cm->MBs;
|
fps.pcnt_second_ref = 1.0 * (double)second_ref_count / cm->MBs;
|
||||||
|
fps.pcnt_neutral = 1.0 * (double)neutral_count / cm->MBs;
|
||||||
|
|
||||||
if (mvcount > 0)
|
if (mvcount > 0)
|
||||||
{
|
{
|
||||||
@@ -1341,7 +1361,7 @@ void vp8_end_second_pass(VP8_COMP *cpi)
|
|||||||
|
|
||||||
// This function gives and estimate of how badly we believe
|
// This function gives and estimate of how badly we believe
|
||||||
// the predicition quality is decaying from frame to frame.
|
// the predicition quality is decaying from frame to frame.
|
||||||
double gf_prediction_decay_rate(VP8_COMP *cpi, FIRSTPASS_STATS *next_frame)
|
double get_prediction_decay_rate(VP8_COMP *cpi, FIRSTPASS_STATS *next_frame)
|
||||||
{
|
{
|
||||||
double prediction_decay_rate;
|
double prediction_decay_rate;
|
||||||
double motion_decay;
|
double motion_decay;
|
||||||
@@ -1376,6 +1396,52 @@ double gf_prediction_decay_rate(VP8_COMP *cpi, FIRSTPASS_STATS *next_frame)
|
|||||||
return prediction_decay_rate;
|
return prediction_decay_rate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Funtion to test for a condition where a complex transition is followe
|
||||||
|
// by a static section. For example in slide shows where there is a fade
|
||||||
|
// between slides. This is to help with more optimal kf and gf positioning.
|
||||||
|
BOOL detect_transition_to_still(
|
||||||
|
VP8_COMP *cpi,
|
||||||
|
int frame_interval,
|
||||||
|
int still_interval,
|
||||||
|
double loop_decay_rate,
|
||||||
|
double decay_accumulator )
|
||||||
|
{
|
||||||
|
BOOL trans_to_still = FALSE;
|
||||||
|
|
||||||
|
// Break clause to detect very still sections after motion
|
||||||
|
// For example a staic image after a fade or other transition
|
||||||
|
// instead of a clean scene cut.
|
||||||
|
if ( (frame_interval > MIN_GF_INTERVAL) &&
|
||||||
|
(loop_decay_rate >= 0.999) &&
|
||||||
|
(decay_accumulator < 0.9) )
|
||||||
|
{
|
||||||
|
int j;
|
||||||
|
FIRSTPASS_STATS * position = cpi->stats_in;
|
||||||
|
FIRSTPASS_STATS tmp_next_frame;
|
||||||
|
double decay_rate;
|
||||||
|
|
||||||
|
// Look ahead a few frames to see if static condition
|
||||||
|
// persists...
|
||||||
|
for ( j = 0; j < still_interval; j++ )
|
||||||
|
{
|
||||||
|
if (EOF == vp8_input_stats(cpi, &tmp_next_frame))
|
||||||
|
break;
|
||||||
|
|
||||||
|
decay_rate = get_prediction_decay_rate(cpi, &tmp_next_frame);
|
||||||
|
if ( decay_rate < 0.999 )
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Reset file position
|
||||||
|
reset_fpf_position(cpi, position);
|
||||||
|
|
||||||
|
// Only if it does do we signal a transition to still
|
||||||
|
if ( j == still_interval )
|
||||||
|
trans_to_still = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return trans_to_still;
|
||||||
|
}
|
||||||
|
|
||||||
// Analyse and define a gf/arf group .
|
// Analyse and define a gf/arf group .
|
||||||
static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
||||||
{
|
{
|
||||||
@@ -1528,7 +1594,7 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
|||||||
if (r > GF_RMAX)
|
if (r > GF_RMAX)
|
||||||
r = GF_RMAX;
|
r = GF_RMAX;
|
||||||
|
|
||||||
loop_decay_rate = gf_prediction_decay_rate(cpi, &next_frame);
|
loop_decay_rate = get_prediction_decay_rate(cpi, &next_frame);
|
||||||
|
|
||||||
// Cumulative effect of decay
|
// Cumulative effect of decay
|
||||||
decay_accumulator = decay_accumulator * loop_decay_rate;
|
decay_accumulator = decay_accumulator * loop_decay_rate;
|
||||||
@@ -1537,48 +1603,13 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
|||||||
boost_score += (decay_accumulator * r);
|
boost_score += (decay_accumulator * r);
|
||||||
|
|
||||||
// Break clause to detect very still sections after motion
|
// Break clause to detect very still sections after motion
|
||||||
// For example a staic image after a fade or other transition
|
// For example a staic image after a fade or other transition.
|
||||||
// instead of a clean key frame.
|
if ( detect_transition_to_still( cpi, i, 5,
|
||||||
if ( (i > MIN_GF_INTERVAL) &&
|
loop_decay_rate, decay_accumulator ) )
|
||||||
(loop_decay_rate >= 0.999) &&
|
|
||||||
(decay_accumulator < 0.9) )
|
|
||||||
{
|
{
|
||||||
int j;
|
allow_alt_ref = FALSE;
|
||||||
FIRSTPASS_STATS * position = cpi->stats_in;
|
boost_score = old_boost_score;
|
||||||
FIRSTPASS_STATS tmp_next_frame;
|
break;
|
||||||
double decay_rate;
|
|
||||||
|
|
||||||
// Look ahead a few frames to see if static condition
|
|
||||||
// persists...
|
|
||||||
for ( j = 0; j < 4; j++ )
|
|
||||||
{
|
|
||||||
if (EOF == vp8_input_stats(cpi, &tmp_next_frame))
|
|
||||||
break;
|
|
||||||
|
|
||||||
decay_rate = gf_prediction_decay_rate(cpi, &tmp_next_frame);
|
|
||||||
if ( decay_rate < 0.999 )
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
reset_fpf_position(cpi, position); // Reset file position
|
|
||||||
|
|
||||||
// Force GF not alt ref
|
|
||||||
if ( j == 4 )
|
|
||||||
{
|
|
||||||
if (0)
|
|
||||||
{
|
|
||||||
FILE *f = fopen("fadegf.stt", "a");
|
|
||||||
fprintf(f, " %8d %8d %10.4f %10.4f %10.4f\n",
|
|
||||||
cpi->common.current_video_frame+i, i,
|
|
||||||
loop_decay_rate, decay_accumulator,
|
|
||||||
boost_score );
|
|
||||||
fclose(f);
|
|
||||||
}
|
|
||||||
|
|
||||||
allow_alt_ref = FALSE;
|
|
||||||
|
|
||||||
boost_score = old_boost_score;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Break out conditions.
|
// Break out conditions.
|
||||||
@@ -2285,7 +2316,7 @@ static BOOL test_candidate_kf(VP8_COMP *cpi, FIRSTPASS_STATS *last_frame, FIRST
|
|||||||
(next_frame->pcnt_second_ref < 0.10) &&
|
(next_frame->pcnt_second_ref < 0.10) &&
|
||||||
((this_frame->pcnt_inter < 0.05) ||
|
((this_frame->pcnt_inter < 0.05) ||
|
||||||
(
|
(
|
||||||
(this_frame->pcnt_inter < .25) &&
|
((this_frame->pcnt_inter - this_frame->pcnt_neutral) < .25) &&
|
||||||
((this_frame->intra_error / DOUBLE_DIVIDE_CHECK(this_frame->coded_error)) < 2.5) &&
|
((this_frame->intra_error / DOUBLE_DIVIDE_CHECK(this_frame->coded_error)) < 2.5) &&
|
||||||
((fabs(last_frame->coded_error - this_frame->coded_error) / DOUBLE_DIVIDE_CHECK(this_frame->coded_error) > .40) ||
|
((fabs(last_frame->coded_error - this_frame->coded_error) / DOUBLE_DIVIDE_CHECK(this_frame->coded_error) > .40) ||
|
||||||
(fabs(last_frame->intra_error - this_frame->intra_error) / DOUBLE_DIVIDE_CHECK(this_frame->intra_error) > .40) ||
|
(fabs(last_frame->intra_error - this_frame->intra_error) / DOUBLE_DIVIDE_CHECK(this_frame->intra_error) > .40) ||
|
||||||
@@ -2332,7 +2363,9 @@ static BOOL test_candidate_kf(VP8_COMP *cpi, FIRSTPASS_STATS *last_frame, FIRST
|
|||||||
// Test various breakout clauses
|
// Test various breakout clauses
|
||||||
if ((local_next_frame.pcnt_inter < 0.05) ||
|
if ((local_next_frame.pcnt_inter < 0.05) ||
|
||||||
(next_iiratio < 1.5) ||
|
(next_iiratio < 1.5) ||
|
||||||
((local_next_frame.pcnt_inter < 0.20) && (next_iiratio < 3.0)) ||
|
(((local_next_frame.pcnt_inter -
|
||||||
|
local_next_frame.pcnt_neutral) < 0.20) &&
|
||||||
|
(next_iiratio < 3.0)) ||
|
||||||
((boost_score - old_boost_score) < 0.5) ||
|
((boost_score - old_boost_score) < 0.5) ||
|
||||||
(local_next_frame.intra_error < 200)
|
(local_next_frame.intra_error < 200)
|
||||||
)
|
)
|
||||||
@@ -2363,13 +2396,13 @@ static BOOL test_candidate_kf(VP8_COMP *cpi, FIRSTPASS_STATS *last_frame, FIRST
|
|||||||
}
|
}
|
||||||
void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
||||||
{
|
{
|
||||||
int i;
|
int i,j;
|
||||||
FIRSTPASS_STATS last_frame;
|
FIRSTPASS_STATS last_frame;
|
||||||
FIRSTPASS_STATS first_frame;
|
FIRSTPASS_STATS first_frame;
|
||||||
FIRSTPASS_STATS next_frame;
|
FIRSTPASS_STATS next_frame;
|
||||||
FIRSTPASS_STATS *start_position;
|
FIRSTPASS_STATS *start_position;
|
||||||
|
|
||||||
double decay_accumulator = 0;
|
double decay_accumulator = 1.0;
|
||||||
double boost_score = 0;
|
double boost_score = 0;
|
||||||
double old_boost_score = 0.0;
|
double old_boost_score = 0.0;
|
||||||
double loop_decay_rate;
|
double loop_decay_rate;
|
||||||
@@ -2379,6 +2412,7 @@ void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
|||||||
double kf_group_intra_err = 0.0;
|
double kf_group_intra_err = 0.0;
|
||||||
double kf_group_coded_err = 0.0;
|
double kf_group_coded_err = 0.0;
|
||||||
double two_pass_min_rate = (double)(cpi->oxcf.target_bandwidth * cpi->oxcf.two_pass_vbrmin_section / 100);
|
double two_pass_min_rate = (double)(cpi->oxcf.target_bandwidth * cpi->oxcf.two_pass_vbrmin_section / 100);
|
||||||
|
double recent_loop_decay[8] = {1.0,1.0,1.0,1.0,1.0,1.0,1.0,1.0};
|
||||||
|
|
||||||
vpx_memset(&next_frame, 0, sizeof(next_frame)); // assure clean
|
vpx_memset(&next_frame, 0, sizeof(next_frame)); // assure clean
|
||||||
|
|
||||||
@@ -2407,6 +2441,7 @@ void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
|||||||
kf_mod_err = calculate_modified_err(cpi, this_frame);
|
kf_mod_err = calculate_modified_err(cpi, this_frame);
|
||||||
|
|
||||||
// find the next keyframe
|
// find the next keyframe
|
||||||
|
i = 0;
|
||||||
while (cpi->stats_in < cpi->stats_in_end)
|
while (cpi->stats_in < cpi->stats_in_end)
|
||||||
{
|
{
|
||||||
// Accumulate kf group error
|
// Accumulate kf group error
|
||||||
@@ -2425,9 +2460,34 @@ void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
|||||||
if (cpi->oxcf.auto_key
|
if (cpi->oxcf.auto_key
|
||||||
&& lookup_next_frame_stats(cpi, &next_frame) != EOF)
|
&& lookup_next_frame_stats(cpi, &next_frame) != EOF)
|
||||||
{
|
{
|
||||||
|
// Normal scene cut check
|
||||||
if (test_candidate_kf(cpi, &last_frame, this_frame, &next_frame))
|
if (test_candidate_kf(cpi, &last_frame, this_frame, &next_frame))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
// How fast is prediction quality decaying
|
||||||
|
loop_decay_rate = get_prediction_decay_rate(cpi, &next_frame);
|
||||||
|
|
||||||
|
// We want to know something about the recent past... rather than
|
||||||
|
// as used elsewhere where we are concened with decay in prediction
|
||||||
|
// quality since the last GF or KF.
|
||||||
|
recent_loop_decay[i%8] = loop_decay_rate;
|
||||||
|
decay_accumulator = 1.0;
|
||||||
|
for (j = 0; j < 8; j++)
|
||||||
|
{
|
||||||
|
decay_accumulator = decay_accumulator * recent_loop_decay[j];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Special check for transition or high motion followed by a
|
||||||
|
// to a static scene.
|
||||||
|
if ( detect_transition_to_still( cpi, i,
|
||||||
|
(cpi->key_frame_frequency-i),
|
||||||
|
loop_decay_rate,
|
||||||
|
decay_accumulator ) )
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Step on to the next frame
|
// Step on to the next frame
|
||||||
cpi->frames_to_key ++;
|
cpi->frames_to_key ++;
|
||||||
|
|
||||||
@@ -2437,6 +2497,8 @@ void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
|||||||
break;
|
break;
|
||||||
} else
|
} else
|
||||||
cpi->frames_to_key ++;
|
cpi->frames_to_key ++;
|
||||||
|
|
||||||
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If there is a max kf interval set by the user we must obey it.
|
// If there is a max kf interval set by the user we must obey it.
|
||||||
@@ -2588,32 +2650,8 @@ void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
|
|||||||
if (r > RMAX)
|
if (r > RMAX)
|
||||||
r = RMAX;
|
r = RMAX;
|
||||||
|
|
||||||
// Adjust loop decay rate
|
// How fast is prediction quality decaying
|
||||||
//if ( next_frame.pcnt_inter < loop_decay_rate )
|
loop_decay_rate = get_prediction_decay_rate(cpi, &next_frame);
|
||||||
loop_decay_rate = next_frame.pcnt_inter;
|
|
||||||
|
|
||||||
// High % motion -> somewhat higher decay rate
|
|
||||||
motion_pct = next_frame.pcnt_motion;
|
|
||||||
motion_decay = (1.0 - (motion_pct / 20.0));
|
|
||||||
if (motion_decay < loop_decay_rate)
|
|
||||||
loop_decay_rate = motion_decay;
|
|
||||||
|
|
||||||
// Adjustment to decay rate based on speed of motion
|
|
||||||
{
|
|
||||||
double this_mv_rabs;
|
|
||||||
double this_mv_cabs;
|
|
||||||
double distance_factor;
|
|
||||||
|
|
||||||
this_mv_rabs = fabs(next_frame.mvr_abs * motion_pct);
|
|
||||||
this_mv_cabs = fabs(next_frame.mvc_abs * motion_pct);
|
|
||||||
|
|
||||||
distance_factor = sqrt((this_mv_rabs * this_mv_rabs) +
|
|
||||||
(this_mv_cabs * this_mv_cabs)) / 250.0;
|
|
||||||
distance_factor = ((distance_factor > 1.0)
|
|
||||||
? 0.0 : (1.0 - distance_factor));
|
|
||||||
if (distance_factor < loop_decay_rate)
|
|
||||||
loop_decay_rate = distance_factor;
|
|
||||||
}
|
|
||||||
|
|
||||||
decay_accumulator = decay_accumulator * loop_decay_rate;
|
decay_accumulator = decay_accumulator * loop_decay_rate;
|
||||||
decay_accumulator = decay_accumulator < 0.1 ? 0.1 : decay_accumulator;
|
decay_accumulator = decay_accumulator < 0.1 ? 0.1 : decay_accumulator;
|
||||||
|
@@ -99,6 +99,7 @@ typedef struct
|
|||||||
double pcnt_inter;
|
double pcnt_inter;
|
||||||
double pcnt_motion;
|
double pcnt_motion;
|
||||||
double pcnt_second_ref;
|
double pcnt_second_ref;
|
||||||
|
double pcnt_neutral;
|
||||||
double MVr;
|
double MVr;
|
||||||
double mvr_abs;
|
double mvr_abs;
|
||||||
double MVc;
|
double MVc;
|
||||||
|
Reference in New Issue
Block a user