Merge "Move firstpass motion map to stats packet"

This commit is contained in:
John Koleszar 2010-10-21 11:09:02 -07:00 коммит произвёл Code Review
Родитель 4cefb4434f bb7dd5b1ba
Коммит 1ee3ebcd66
5 изменённых файлов: 115 добавлений и 119 удалений

Просмотреть файл

@ -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 void reset_fpf_position(VP8_COMP *cpi, FIRSTPASS_STATS *Position)
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 @@ static int lookup_next_frame_stats(VP8_COMP *cpi, FIRSTPASS_STATS *next_frame)
// 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 @@ int frame_max_bits(VP8_COMP *cpi)
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 @@ int frame_max_bits(VP8_COMP *cpi)
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 @@ void vp8_output_stats(struct vpx_codec_pkt_list *pktlist,
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 @@ void vp8_avg_stats(FIRSTPASS_STATS *section)
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 @@ void vp8_init_first_pass(VP8_COMP *cpi)
FILE *fpfile;
fpfile = fopen("firstpass.stt", "w");
fclose(fpfile);
fpfile = fopen("fpmotionmap.stt", "wb");
fclose(fpfile);
}
#endif
@ -419,16 +425,10 @@ void vp8_init_first_pass(VP8_COMP *cpi)
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 @@ void vp8_first_pass(VP8_COMP *cpi)
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 @@ void vp8_init_second_pass(VP8_COMP *cpi)
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 @@ void vp8_init_second_pass(VP8_COMP *cpi)
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 @@ void vp8_init_second_pass(VP8_COMP *cpi)
}
#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 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
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 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
// 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 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
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 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
// Read / create a motion map for the region of interest
vp8_input_fpmm(cpi);
}
#endif
}
else
{
@ -1581,7 +1566,7 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
// 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 @@ static void define_gf_group(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
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 @@ static void assign_std_frame_bits(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
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 @@ static void assign_std_frame_bits(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
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 @@ void vp8_second_pass(VP8_COMP *cpi)
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 @@ void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
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 @@ void vp8_find_next_key_frame(VP8_COMP *cpi, FIRSTPASS_STATS *this_frame)
}
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;

Просмотреть файл

@ -20,4 +20,5 @@ extern void vp8_init_second_pass(VP8_COMP *cpi);
extern void vp8_second_pass(VP8_COMP *cpi);
extern void vp8_end_second_pass(VP8_COMP *cpi);
extern size_t vp8_firstpass_stats_sz(unsigned int mb_count);
#endif

Просмотреть файл

@ -330,6 +330,8 @@ void vp8_dealloc_compressor_data(VP8_COMP *cpi)
cpi->mb.pip = 0;
vpx_free(cpi->total_stats);
vpx_free(cpi->this_frame_stats);
}
static void enable_segmentation(VP8_PTR ptr)
@ -1392,6 +1394,12 @@ void vp8_alloc_compressor_data(VP8_COMP *cpi)
CHECK_MEM_ERROR(cpi->gf_active_flags, vpx_calloc(1, cm->mb_rows * cm->mb_cols));
cpi->gf_active_count = cm->mb_rows * cm->mb_cols;
cpi->total_stats = vpx_calloc(1, vp8_firstpass_stats_sz(cpi->common.MBs));
cpi->this_frame_stats = vpx_calloc(1, vp8_firstpass_stats_sz(cpi->common.MBs));
if(!cpi->total_stats || !cpi->this_frame_stats)
vpx_internal_error(&cpi->common.error, VPX_CODEC_MEM_ERROR,
"Failed to allocate firstpass stats");
}
@ -2290,10 +2298,12 @@ VP8_PTR vp8_create_compressor(VP8_CONFIG *oxcf)
}
else if (cpi->pass == 2)
{
size_t packet_sz = vp8_firstpass_stats_sz(cpi->common.MBs);
int packets = oxcf->two_pass_stats_in.sz / packet_sz;
cpi->stats_in = oxcf->two_pass_stats_in.buf;
cpi->stats_in_end = cpi->stats_in
+ oxcf->two_pass_stats_in.sz / sizeof(FIRSTPASS_STATS)
- 1;
cpi->stats_in_end = (void*)((char *)cpi->stats_in
+ (packets - 1) * packet_sz);
vp8_init_second_pass(cpi);
}
@ -3481,7 +3491,7 @@ static void apply_temporal_filter
modifier = 16 - modifier;
#endif
modifier *= filter_weight;
count[k] += modifier;
accumulator[k] += modifier * pixel_value;
@ -3656,7 +3666,7 @@ static void vp8cx_temp_blur1_c
YV12_BUFFER_CONFIG *f = cpi->frames[alt_ref_index];
unsigned char *dst1, *dst2;
DECLARE_ALIGNED(16, unsigned char, predictor[384]);
// Save input state
unsigned char *y_buffer = mbd->pre.y_buffer;
unsigned char *u_buffer = mbd->pre.u_buffer;

Просмотреть файл

@ -461,14 +461,14 @@ typedef struct
int target_bandwidth;
long long bits_left;
FIRSTPASS_STATS total_stats;
FIRSTPASS_STATS this_frame_stats;
FIRSTPASS_STATS *total_stats;
FIRSTPASS_STATS *this_frame_stats;
FIRSTPASS_STATS *stats_in, *stats_in_end;
struct vpx_codec_pkt_list *output_pkt_list;
int first_pass_done;
unsigned char *fp_motion_map;
FILE *fp_motion_mapfile;
int fpmm_pos;
unsigned char *fp_motion_map_stats, *fp_motion_map_stats_save;
#if 0
// Experimental code for lagged and one pass

Просмотреть файл

@ -14,6 +14,7 @@
#include "vpx_version.h"
#include "onyx_int.h"
#include "vpx/vp8e.h"
#include "vp8/encoder/firstpass.h"
#include "onyx.h"
#include <stdlib.h>
#include <string.h>
@ -189,22 +190,25 @@ static vpx_codec_err_t validate_config(vpx_codec_alg_priv_t *ctx,
if (cfg->g_pass == VPX_RC_LAST_PASS)
{
int n_doubles = cfg->rc_twopass_stats_in.sz / sizeof(double);
int n_packets = cfg->rc_twopass_stats_in.sz / sizeof(FIRSTPASS_STATS);
double frames;
int mb_r = (cfg->g_h + 15) / 16;
int mb_c = (cfg->g_w + 15) / 16;
size_t packet_sz = vp8_firstpass_stats_sz(mb_r * mb_c);
int n_packets = cfg->rc_twopass_stats_in.sz / packet_sz;
FIRSTPASS_STATS *stats;
if (!cfg->rc_twopass_stats_in.buf)
ERROR("rc_twopass_stats_in.buf not set.");
if (cfg->rc_twopass_stats_in.sz % sizeof(FIRSTPASS_STATS))
if (cfg->rc_twopass_stats_in.sz % packet_sz)
ERROR("rc_twopass_stats_in.sz indicates truncated packet.");
if (cfg->rc_twopass_stats_in.sz < 2 * sizeof(FIRSTPASS_STATS))
if (cfg->rc_twopass_stats_in.sz < 2 * packet_sz)
ERROR("rc_twopass_stats_in requires at least two packets.");
frames = ((double *)cfg->rc_twopass_stats_in.buf)[n_doubles - 1];
stats = (void*)((char *)cfg->rc_twopass_stats_in.buf
+ (n_packets - 1) * packet_sz);
if ((int)(frames + 0.5) != n_packets - 1)
if ((int)(stats->count + 0.5) != n_packets - 1)
ERROR("rc_twopass_stats_in missing EOS stats packet");
}