Move firstpass motion map to stats packet
The first implementation of the firstpass motion map for motion
compensated temporal filtering created a file, fpmotionmap.stt,
in the current working directory. This was not safe for multiple
encoder instances. This patch merges this data into the first pass
stats packet interface, so that it is handled like the other
(numerical) firstpass stats.
The new stats packet is defined as follows:
Numerical Stats (16 doubles) -- 128 bytes
Motion Map -- 1 byte / Macroblock
Padding -- to align packet to 8 bytes
The fpmotionmap.stt file can still be generated for debugging
purposes in the same way that the textual version of the stats
are available (defining OUTPUT_FPF in firstpass.c)
Change-Id: I083ffbfd95e7d6a42bb4039ba0e81f678c8183ca
diff --git a/vp8/encoder/firstpass.c b/vp8/encoder/firstpass.c
index 13633e9..607c3d2 100644
--- a/vp8/encoder/firstpass.c
+++ b/vp8/encoder/firstpass.c
@@ -30,7 +30,6 @@
#include "encodemv.h"
//#define OUTPUT_FPF 1
-#define FIRSTPASS_MM 1
#if CONFIG_RUNTIME_CPU_DETECT
#define IF_RTCD(x) (x)
@@ -108,15 +107,6 @@
static int lookup_next_frame_stats(VP8_COMP *cpi, FIRSTPASS_STATS *next_frame)
{
- /*FIRSTPASS_STATS * start_pos;
- int ret_val;
-
- start_pos = cpi->stats_in;
- ret_val = vp8_input_stats(cpi, next_frame);
- reset_fpf_position(cpi, start_pos);
-
- return ret_val;*/
-
if (cpi->stats_in >= cpi->stats_in_end)
return EOF;
@@ -127,7 +117,7 @@
// Calculate a modified Error used in distributing bits between easier and harder frames
static double calculate_modified_err(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
{
- double av_err = cpi->total_stats.ssim_weighted_pred_err;
+ double av_err = cpi->total_stats->ssim_weighted_pred_err;
double this_err = this_frame->ssim_weighted_pred_err;
double modified_err;
@@ -238,7 +228,7 @@
else
{
// For VBR base this on the bits and frames left plus the two_pass_vbrmax_section rate passed in by the user
- max_bits = (int)(((double)cpi->bits_left / (cpi->total_stats.count - (double)cpi->common.current_video_frame)) * ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0));
+ max_bits = (int)(((double)cpi->bits_left / (cpi->total_stats->count - (double)cpi->common.current_video_frame)) * ((double)cpi->oxcf.two_pass_vbrmax_section / 100.0));
}
// Trap case where we are out of bits
@@ -248,13 +238,31 @@
return max_bits;
}
-void vp8_output_stats(struct vpx_codec_pkt_list *pktlist,
+
+extern size_t vp8_firstpass_stats_sz(unsigned int mb_count)
+{
+ /* Calculate the size of a stats packet, which is dependent on the frame
+ * resolution. The FIRSTPASS_STATS struct has a single element array,
+ * motion_map, which is virtually expanded to have one element per
+ * macroblock.
+ */
+ size_t stats_sz;
+ FIRSTPASS_STATS stats;
+
+ stats_sz = sizeof(FIRSTPASS_STATS) + mb_count;
+ stats_sz = (stats_sz + 7) & ~7;
+ return stats_sz;
+}
+
+
+void vp8_output_stats(const VP8_COMP *cpi,
+ struct vpx_codec_pkt_list *pktlist,
FIRSTPASS_STATS *stats)
{
struct vpx_codec_cx_pkt pkt;
pkt.kind = VPX_CODEC_STATS_PKT;
pkt.data.twopass_stats.buf = stats;
- pkt.data.twopass_stats.sz = sizeof(*stats);
+ pkt.data.twopass_stats.sz = vp8_firstpass_stats_sz(cpi->common.MBs);
vpx_codec_pkt_list_add(pktlist, &pkt);
// TEMP debug code
@@ -280,16 +288,24 @@
stats->mv_in_out_count,
stats->count);
fclose(fpfile);
+
+
+ fpfile = fopen("fpmotionmap.stt", "a");
+ fwrite(cpi->fp_motion_map, 1, cpi->common.MBs, fpfile);
+ fclose(fpfile);
}
#endif
}
int vp8_input_stats(VP8_COMP *cpi, FIRSTPASS_STATS *fps)
{
+ size_t stats_sz = vp8_firstpass_stats_sz(cpi->common.MBs);
+
if (cpi->stats_in >= cpi->stats_in_end)
return EOF;
- *fps = *cpi->stats_in++;
+ *fps = *cpi->stats_in;
+ cpi->stats_in = (void*)((char *)cpi->stats_in + stats_sz);
return 1;
}
@@ -352,59 +368,47 @@
section->duration /= section->count;
}
-int vp8_fpmm_get_pos(VP8_COMP *cpi)
+unsigned char *vp8_fpmm_get_pos(VP8_COMP *cpi)
{
- return ftell(cpi->fp_motion_mapfile);
+ return cpi->fp_motion_map_stats;
}
-void vp8_fpmm_reset_pos(VP8_COMP *cpi, int target_pos)
+void vp8_fpmm_reset_pos(VP8_COMP *cpi, unsigned char *target_pos)
{
int Offset;
- if (cpi->fp_motion_mapfile)
- {
- Offset = ftell(cpi->fp_motion_mapfile) - target_pos;
- fseek(cpi->fp_motion_mapfile, (int) - Offset, SEEK_CUR);
- }
+ cpi->fp_motion_map_stats = target_pos;
}
void vp8_advance_fpmm(VP8_COMP *cpi, int count)
{
-#if FIRSTPASS_MM
- fseek(cpi->fp_motion_mapfile, (int)(count * cpi->common.MBs), SEEK_CUR);
-#endif
+ cpi->fp_motion_map_stats = (void*)((char*)cpi->fp_motion_map_stats +
+ count * vp8_firstpass_stats_sz(cpi->common.MBs));
}
void vp8_input_fpmm(VP8_COMP *cpi)
{
-#if FIRSTPASS_MM
+ unsigned char *fpmm = cpi->fp_motion_map;
int MBs = cpi->common.MBs;
int max_frames = cpi->active_arnr_frames;
+ int i;
- if (!cpi->fp_motion_mapfile)
- return; // Error
-
- // Read the specified number of frame motion maps
- if (fread(cpi->fp_motion_map, 1,
- max_frames * MBs,
- cpi->fp_motion_mapfile) != max_frames*MBs)
+ for (i=0; i<max_frames; i++)
{
- // Read error
- return;
+ char *motion_map = (char*)cpi->fp_motion_map_stats
+ + sizeof(FIRSTPASS_STATS);
+
+ memcpy(fpmm, motion_map, MBs);
+ fpmm += MBs;
+ vp8_advance_fpmm(cpi, 1);
}
// Flag the use of weights in the temporal filter
cpi->use_weighted_temporal_filter = 1;
-
-#endif
}
void vp8_init_first_pass(VP8_COMP *cpi)
{
- vp8_zero_stats(&cpi->total_stats);
-
-#ifdef FIRSTPASS_MM
- cpi->fp_motion_mapfile = fopen("fpmotionmap.stt", "wb");
-#endif
+ vp8_zero_stats(cpi->total_stats);
// TEMP debug code
#ifdef OUTPUT_FPF
@@ -412,6 +416,8 @@
FILE *fpfile;
fpfile = fopen("firstpass.stt", "w");
fclose(fpfile);
+ fpfile = fopen("fpmotionmap.stt", "wb");
+ fclose(fpfile);
}
#endif
@@ -419,16 +425,10 @@
void vp8_end_first_pass(VP8_COMP *cpi)
{
- vp8_output_stats(cpi->output_pkt_list, &cpi->total_stats);
-
-#if FIRSTPASS_MM
-
- if (cpi->fp_motion_mapfile)
- fclose(cpi->fp_motion_mapfile);
-
-#endif
-
+ vp8_output_stats(cpi, cpi->output_pkt_list, cpi->total_stats);
}
+
+
void vp8_zz_motion_search( VP8_COMP *cpi, MACROBLOCK * x, YV12_BUFFER_CONFIG * recon_buffer, int * best_motion_err, int recon_yoffset )
{
MACROBLOCKD * const xd = & x->e_mbd;
@@ -839,19 +839,20 @@
fps.duration = cpi->source_end_time_stamp - cpi->source_time_stamp;
// don't want to do outputstats with a stack variable!
- cpi->this_frame_stats = fps;
- vp8_output_stats(cpi->output_pkt_list, &cpi->this_frame_stats);
- vp8_accumulate_stats(&cpi->total_stats, &fps);
-
-#if FIRSTPASS_MM
- fwrite(cpi->fp_motion_map, 1, cpi->common.MBs, cpi->fp_motion_mapfile);
-#endif
+ memcpy(cpi->this_frame_stats,
+ &fps,
+ sizeof(FIRSTPASS_STATS));
+ memcpy((char*)cpi->this_frame_stats + sizeof(FIRSTPASS_STATS),
+ cpi->fp_motion_map,
+ sizeof(cpi->fp_motion_map[0]) * cpi->common.MBs);
+ vp8_output_stats(cpi, cpi->output_pkt_list, cpi->this_frame_stats);
+ vp8_accumulate_stats(cpi->total_stats, &fps);
}
// Copy the previous Last Frame into the GF buffer if specific conditions for doing so are met
if ((cm->current_video_frame > 0) &&
- (cpi->this_frame_stats.pcnt_inter > 0.20) &&
- ((cpi->this_frame_stats.intra_error / cpi->this_frame_stats.coded_error) > 2.0))
+ (cpi->this_frame_stats->pcnt_inter > 0.20) &&
+ ((cpi->this_frame_stats->intra_error / cpi->this_frame_stats->coded_error) > 2.0))
{
vp8_yv12_copy_frame_ptr(lst_yv12, gld_yv12);
}
@@ -1120,33 +1121,33 @@
double two_pass_min_rate = (double)(cpi->oxcf.target_bandwidth * cpi->oxcf.two_pass_vbrmin_section / 100);
- vp8_zero_stats(&cpi->total_stats);
+ vp8_zero_stats(cpi->total_stats);
if (!cpi->stats_in_end)
return;
- cpi->total_stats = *cpi->stats_in_end;
+ *cpi->total_stats = *cpi->stats_in_end;
- cpi->total_error_left = cpi->total_stats.ssim_weighted_pred_err;
- cpi->total_intra_error_left = cpi->total_stats.intra_error;
- cpi->total_coded_error_left = cpi->total_stats.coded_error;
+ cpi->total_error_left = cpi->total_stats->ssim_weighted_pred_err;
+ cpi->total_intra_error_left = cpi->total_stats->intra_error;
+ cpi->total_coded_error_left = cpi->total_stats->coded_error;
cpi->start_tot_err_left = cpi->total_error_left;
- //cpi->bits_left = (long long)(cpi->total_stats.count * cpi->oxcf.target_bandwidth / DOUBLE_DIVIDE_CHECK((double)cpi->oxcf.frame_rate));
- //cpi->bits_left -= (long long)(cpi->total_stats.count * two_pass_min_rate / DOUBLE_DIVIDE_CHECK((double)cpi->oxcf.frame_rate));
+ //cpi->bits_left = (long long)(cpi->total_stats->count * cpi->oxcf.target_bandwidth / DOUBLE_DIVIDE_CHECK((double)cpi->oxcf.frame_rate));
+ //cpi->bits_left -= (long long)(cpi->total_stats->count * two_pass_min_rate / DOUBLE_DIVIDE_CHECK((double)cpi->oxcf.frame_rate));
// 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.
// Its calculated based on the actual durations of all frames from the first
// pass.
- vp8_new_frame_rate(cpi, 10000000.0 * cpi->total_stats.count / cpi->total_stats.duration);
+ vp8_new_frame_rate(cpi, 10000000.0 * cpi->total_stats->count / cpi->total_stats->duration);
cpi->output_frame_rate = cpi->oxcf.frame_rate;
- cpi->bits_left = (long long)(cpi->total_stats.duration * cpi->oxcf.target_bandwidth / 10000000.0) ;
- cpi->bits_left -= (long long)(cpi->total_stats.duration * two_pass_min_rate / 10000000.0);
+ cpi->bits_left = (long long)(cpi->total_stats->duration * cpi->oxcf.target_bandwidth / 10000000.0) ;
+ cpi->bits_left -= (long long)(cpi->total_stats->duration * two_pass_min_rate / 10000000.0);
- vp8_avg_stats(&cpi->total_stats);
+ vp8_avg_stats(cpi->total_stats);
// Scan the first pass file and calculate an average Intra / Inter error score ratio for the sequence
{
@@ -1162,7 +1163,7 @@
sum_iiratio += IIRatio;
}
- cpi->avg_iiratio = sum_iiratio / DOUBLE_DIVIDE_CHECK((double)cpi->total_stats.count);
+ cpi->avg_iiratio = sum_iiratio / DOUBLE_DIVIDE_CHECK((double)cpi->total_stats->count);
// Reset file position
reset_fpf_position(cpi, start_pos);
@@ -1184,21 +1185,11 @@
}
-#if FIRSTPASS_MM
- cpi->fp_motion_mapfile = 0;
- cpi->fp_motion_mapfile = fopen("fpmotionmap.stt", "rb");
-#endif
-
+ cpi->fp_motion_map_stats = (unsigned char *)cpi->stats_in;
}
void vp8_end_second_pass(VP8_COMP *cpi)
{
-#if FIRSTPASS_MM
-
- if (cpi->fp_motion_mapfile)
- fclose(cpi->fp_motion_mapfile);
-
-#endif
}
// Analyse and define a gf/arf group .
@@ -1231,18 +1222,14 @@
int max_bits = frame_max_bits(cpi); // Max for a single frame
-#if FIRSTPASS_MM
- int fpmm_pos;
-#endif
+ unsigned char *fpmm_pos;
cpi->gf_group_bits = 0;
cpi->gf_decay_rate = 0;
vp8_clear_system_state(); //__asm emms;
-#if FIRSTPASS_MM
fpmm_pos = vp8_fpmm_get_pos(cpi);
-#endif
start_pos = cpi->stats_in;
@@ -1494,7 +1481,7 @@
// Note: this_frame->frame has been updated in the loop
// so it now points at the ARF frame.
half_gf_int = cpi->baseline_gf_interval >> 1;
- frames_after_arf = cpi->total_stats.count - this_frame->frame - 1;
+ frames_after_arf = cpi->total_stats->count - this_frame->frame - 1;
switch (cpi->oxcf.arnr_type)
{
@@ -1531,12 +1518,11 @@
cpi->active_arnr_frames = frames_bwd + 1 + frames_fwd;
-#if FIRSTPASS_MM
{
// Advance to & read in the motion map for those frames
// to be considered for filtering based on the position
// of the ARF
- vp8_fpmm_reset_pos(cpi, cpi->fpmm_pos);
+ vp8_fpmm_reset_pos(cpi, cpi->fp_motion_map_stats_save);
// Position at the 'earliest' frame to be filtered
vp8_advance_fpmm(cpi,
@@ -1545,7 +1531,6 @@
// Read / create a motion map for the region of interest
vp8_input_fpmm(cpi);
}
-#endif
}
else
{
@@ -1581,7 +1566,7 @@
// Now decide how many bits should be allocated to the GF group as a proportion of those remaining in the kf group.
// The final key frame group in the clip is treated as a special case where cpi->kf_group_bits is tied to cpi->bits_left.
// This is also important for short clips where there may only be one key frame.
- if (cpi->frames_to_key >= (int)(cpi->total_stats.count - cpi->common.current_video_frame))
+ if (cpi->frames_to_key >= (int)(cpi->total_stats->count - cpi->common.current_video_frame))
{
cpi->kf_group_bits = (cpi->bits_left > 0) ? cpi->bits_left : 0;
}
@@ -1781,10 +1766,8 @@
reset_fpf_position(cpi, start_pos);
}
-#if FIRSTPASS_MM
// Reset the First pass motion map file position
vp8_fpmm_reset_pos(cpi, fpmm_pos);
-#endif
}
// Allocate bits to a normal frame that is neither a gf an arf or a key frame.
@@ -1798,7 +1781,7 @@
int max_bits = frame_max_bits(cpi); // Max for a single frame
// The final few frames have special treatment
- if (cpi->frames_till_gf_update_due >= (int)(cpi->total_stats.count - cpi->common.current_video_frame))
+ if (cpi->frames_till_gf_update_due >= (int)(cpi->total_stats->count - cpi->common.current_video_frame))
{
cpi->gf_group_bits = (cpi->bits_left > 0) ? cpi->bits_left : 0;;
}
@@ -1843,7 +1826,7 @@
void vp8_second_pass(VP8_COMP *cpi)
{
int tmp_q;
- int frames_left = (int)(cpi->total_stats.count - cpi->common.current_video_frame);
+ int frames_left = (int)(cpi->total_stats->count - cpi->common.current_video_frame);
FIRSTPASS_STATS this_frame;
FIRSTPASS_STATS this_frame_copy;
@@ -1866,14 +1849,12 @@
if (EOF == vp8_input_stats(cpi, &this_frame))
return;
-#if FIRSTPASS_MM
vpx_memset(cpi->fp_motion_map, 0,
cpi->oxcf.arnr_max_frames*cpi->common.MBs);
- cpi->fpmm_pos = vp8_fpmm_get_pos(cpi);
+ cpi->fp_motion_map_stats_save = vp8_fpmm_get_pos(cpi);
// Step over this frame's first pass motion map
vp8_advance_fpmm(cpi, 1);
-#endif
this_frame_error = this_frame.ssim_weighted_pred_err;
this_frame_intra_error = this_frame.intra_error;
@@ -2562,7 +2543,7 @@
cpi->common.vert_scale = NORMAL;
// Calculate Average bits per frame.
- //av_bits_per_frame = cpi->bits_left/(double)(cpi->total_stats.count - cpi->common.current_video_frame);
+ //av_bits_per_frame = cpi->bits_left/(double)(cpi->total_stats->count - cpi->common.current_video_frame);
av_bits_per_frame = cpi->oxcf.target_bandwidth / DOUBLE_DIVIDE_CHECK((double)cpi->oxcf.frame_rate);
//if ( av_bits_per_frame < 0.0 )
// av_bits_per_frame = 0.0
@@ -2625,7 +2606,7 @@
}
else
{
- long long clip_bits = (long long)(cpi->total_stats.count * cpi->oxcf.target_bandwidth / DOUBLE_DIVIDE_CHECK((double)cpi->oxcf.frame_rate));
+ long long clip_bits = (long long)(cpi->total_stats->count * cpi->oxcf.target_bandwidth / DOUBLE_DIVIDE_CHECK((double)cpi->oxcf.frame_rate));
long long over_spend = cpi->oxcf.starting_buffer_level - cpi->buffer_level;
long long over_spend2 = cpi->oxcf.starting_buffer_level - projected_buffer_level;