#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <float.h>
#include <windows.h>
#include <winreg.h>
#include <vfw.h>
#include <limits.h>
#include <time.h>
#include <signal.h>
#include "filename.h"
#include "vfapi_read.h"
#include "vfw_read.h"
#include "check_block.h"
#include "compressor.h"
#include "bgr24.h"

#define DEFAULT_RATE  3000
#define DEFAULT_KEY   8
#define DEFAULT_CC    75
#define DEFAULT_DIFF  9
#define DEFAULT_PIXEL 28
#define MAX_RATE      60000
#define MAX_KEY       9999
#define MAX_CC        100
#define MAX_DIFF      64
#define MAX_PIXEL     64
#define MIN_RATE      1
#define MIN_KEY       1
#define MIN_CC        1
#define MIN_DIFF      4
#define MIN_PIXEL     4

#define DESC "\nAuto 2 pass MsPEG4 compressor - \"Spaghetti 21st Type-B\" (2002, 1/25)\n"
#define COPYRIGHT "    (C) Tatibana Advanced Research Institute & MARUMO\n\n" 

#define LOG_HEADER               " frame   size  key\n"
#define LOG_FORMAT               "%6d %6d  %s\n"
#define LOG_FORMAT_SCENE_CHANGE  "%6d %6d  %s - scene change (size %6d)\n"
#define LOG_FORMAT_BAD_BLOCK     "%6d %6d  %s - bad block (X:%4d, Y:%4d)\n"

typedef struct {

	/* codec configuration           */
	FOURCC version; /* codec version */
	int    rate;    /* kbits / sec   */
	int    key;     /* seconds       */
	int    cc;      /* 1 - 100       */

	/* block noise checker threshold */
	int diff;
	int pixel;

	/* application setting */
	int loging;
	int verbose;
	int bad_block_report;

	int scene_change_detector;
	/*
	 * this option sets the limit of delta_frame size.
	 * max_size is calcurated by the following equation.
	 *
	 *  max_size = (width * height / 64) * scene_change_detector;
	 *
	 */
	
	int expand_frame_rate;
	/*
	 * this option indicates 60 fps output.
	 *
	 * ex.
	 *  24fps to 60fps (o  o o  o  -> oxxoxoxxox)
	 *  30fps to 60fps (o o o o o  -> oxoxoxoxox)
	 *   [ o - frame, x - null frame ]
	 */

} options;

typedef struct {
	PAVIFILE      file;
	PAVISTREAM    video;
	AVISTREAMINFO avi_format;

	LPBITMAPINFO  original_format;
	LPBITMAPINFO  compress_format;

	COMPRESSOR    encoder;

	LPVOID        encoded_data;
	LPVOID        decoded_data;

	int           key;
	size_t        size;

	HIC           decoder;

	unsigned int  delta_frame_max_size;

	int           expand_frame_rate;
	
}MPEG4_OUT;
#define EXPAND_FRAME_RATE_NO       0
#define EXPAND_FRAME_RATE_24_to_60 1
#define EXPAND_FRAME_RATE_30_to_60 2


typedef struct {
	void         * this;
	int         (* get_frame)(void *, LPVOID *, LPBITMAPINFOHEADER *);
	void        (* release)(void *);
	unsigned int   max_frame;
}VIDEO_INPUT;

void print_usage(char *name, FILE *out);
void print_option(FILE *out);
int parse_option(int argc, char **argv, options *opt);
void check_option(options *opt);

void print_time(time_t time, FILE *out);
time_t calc_rest_time(int current, int all, time_t start);
void print_size(size_t bytes, FILE *out);

int initialize_mpeg4out(MPEG4_OUT *p);
int open_mpeg4out(char *filename, MPEG4_OUT *p, AVISTREAMINFO *avi_format, BITMAPINFOHEADER *frame_format, options *opt);
void compress_force_key_frame(MPEG4_OUT *p, LPVOID data);
int close_mpeg4out(MPEG4_OUT *p);

LPBITMAPINFO copy_bitmap_info(LPBITMAPINFOHEADER in);

void bad_block_report(LPVOID original, LPVOID result, BITMAPINFOHEADER *format, int x, int y, int diff, int pixel, int frame);
	
void m4c(char *infile, char *outfile, options *opt);
void _cdecl sigint_handler(int sig);

int main(int argc, char **argv)
{
	int n;
	options opt;

	n = parse_option(argc, argv, &opt);
	check_option(&opt);
	
	if((argc - n) < 2){
		print_usage(argv[0], stderr);
		if(argc != 1){
			fprintf(stderr, "\nERROR - input file & output file are required\n");
		}
		exit(EXIT_FAILURE);
	}

	AVIFileInit(); /* initialize VfW library */ 

	m4c(argv[argc-2], argv[argc-1], &opt);

	AVIFileExit(); /* release resources */
	
	return EXIT_SUCCESS;
}

void print_usage(char *name, FILE *out)
{
	fprintf(out, DESC);
	fprintf(out, COPYRIGHT);
	fprintf(out, "USAGE: %s [options] in.avi[/aup/tpr/.. ] out.avi\n", read_filename(name));
	
	print_option(out);
}

void print_option(FILE *out)
{
	fprintf(out, "options:\n");
	fprintf(out, "  compressor configuration:\n");
	fprintf(out, "    -v C  select codec,        default: 2 (1:MPG4,2:MP42,3:MP43,L:DIV3,F:DIV4)\n");
	fprintf(out, "    -r N  bit rate,            default: %d kbps\n", DEFAULT_RATE);
	fprintf(out, "    -k N  key frame,           default: %d sec\n", DEFAULT_KEY);
	fprintf(out, "    -c N  compression ctrl,    default: %d %%\n", DEFAULT_CC);
	fprintf(out, "  block noise detector:\n");
	fprintf(out, "    -d N  difference,          default: %2d (range: %d - %d)\n", DEFAULT_DIFF, MIN_DIFF, MAX_DIFF);
	fprintf(out, "    -p N  pixel,               default: %2d (range: %d - %d)\n", DEFAULT_PIXEL, MIN_PIXEL, MAX_PIXEL);
	fprintf(out, "  application setting:\n");
	fprintf(out, "    -s N  scene change detect, default: 0 (0:NO,8:HIGH,12:MID,16:LOW,.. )\n");
	fprintf(out, "    -b    analyze bad block\n");
	fprintf(out, "    -l    loging\n");
	fprintf(out, "    -e    expand frame rate (60fps output)\n");
	fprintf(out, "    -q    quiet\n");
	fprintf(out, "    -h    print this message\n");
}

int parse_option(int argc, char **argv, options *opt)
{
	int i,j,n;
	int c;

	opt->version = mmioFOURCC('M', 'P', '4', '2');
	opt->rate = DEFAULT_RATE;
	opt->key = DEFAULT_KEY;
	opt->cc = DEFAULT_CC;
	opt->loging = 0;
	opt->verbose = 1;
	opt->scene_change_detector = 0;
	opt->bad_block_report = 0;
	opt->expand_frame_rate = 0;
	opt->diff = DEFAULT_DIFF;
	opt->pixel = DEFAULT_PIXEL;

	for(i=1;i<argc;i++){
		if(argv[i][0] != '-'){
			break;
		}
		n = strlen(argv[i]);
		for(j=1;j<n;j++){
			switch(argv[i][j]){
			case 'v':
				if(n-j>1){
					c = argv[i][j+1] & 0xFF;
					j += 2;
				}else{
					c = argv[i+1][0] & 0xFF;
					i++;
				}
				switch(c){
				case '1':
					opt->version = mmioFOURCC('M', 'P', 'G', '4');
					break;
				case '2':
					opt->version = mmioFOURCC('M', 'P', '4', '2');
					break;
				case '3':
					opt->version = mmioFOURCC('M', 'P', '4', '3');
					break;
				case 'l':
				case 'L':
					opt->version = mmioFOURCC('D', 'I', 'V', '3');
					break;
				case 'f':
				case 'F':
					opt->version = mmioFOURCC('D', 'I', 'V', '4');
					break;
				default:
					opt->version = mmioFOURCC('M', 'P', '4', '2');
				}					
				break;
			case 'r':
				if(n-j>1){
					sscanf(argv[i]+j+1, "%d", &(opt->rate));
					j = n;
				}else{
					sscanf(argv[i+1], "%d", &(opt->rate));
					i++;
				}
				break;
			case 'k':
				if(n-j>1){
					sscanf(argv[i]+j+1, "%d", &(opt->key));
					j = n;
				}else{
					sscanf(argv[i+1], "%d", &(opt->key));
					i++;
				}
				break;
			case 'c':
				if(n-j>1){
					sscanf(argv[i]+j+1, "%d", &(opt->cc));
					j = n;
				}else{
					sscanf(argv[i+1], "%d", &(opt->cc));
					i++;
				}
				break;
			case 'd':
				if(n-j>1){
					sscanf(argv[i]+j+1, "%d", &(opt->diff));
					j = n;
				}else{
					sscanf(argv[i+1], "%d", &(opt->diff));
					i++;
				}
				break;
			case 'p':
				if(n-j>1){
					sscanf(argv[i]+j+1, "%d", &(opt->pixel));
					j = n;
				}else{
					sscanf(argv[i+1], "%d", &(opt->pixel));
					i++;
				}
				break;
			case 'l':
				opt->loging = 1;
				break;
			case 'e':
				opt->expand_frame_rate = 1;
				break;
			case 'q':
				opt->verbose = 0;
				break;
			case 'h':
				print_usage(argv[0], stderr);
				exit(EXIT_SUCCESS);
			case 's':
				if(n-j>1){
					sscanf(argv[i]+j+1, "%d", &(opt->scene_change_detector));
					j = n;
				}else{
					sscanf(argv[i+1], "%d", &(opt->scene_change_detector));
					i++;
				}
				break;
			case 'b':
				opt->bad_block_report = 1;
				break;
			default:
				fprintf(stderr, "ERROR - Invalid option \"%c\" specified\n", argv[i][j]);
				print_option(stderr);
				exit(EXIT_FAILURE);
			}
		}
	}

	return i;
}

void check_option(options *opt)
{
	if(opt->rate > MAX_RATE){
		opt->rate = MAX_RATE;
	}else if(opt->rate < MIN_RATE){
		opt->rate = MIN_RATE;
	}

	if(opt->key > MAX_KEY){
		opt->key = MAX_KEY;
	}else if(opt->key < MIN_KEY){
		opt->key = MIN_KEY;
	}

	if(opt->cc > MAX_CC){
		opt->cc = MAX_CC;
	}else if(opt->cc < MIN_CC){
		opt->cc = MIN_CC;
	}

	if(opt->diff > MAX_DIFF){
		opt->diff = MAX_DIFF;
	}else if(opt->diff < MIN_DIFF){
		opt->diff = MIN_DIFF;
	}

	if(opt->pixel > MAX_PIXEL){
		opt->pixel = MAX_PIXEL;
	}else if(opt->pixel < MIN_PIXEL){
		opt->pixel = MIN_PIXEL;
	}
	
	return;
}

int initialize_mpeg4out(MPEG4_OUT *p)
{
	memset(p, 0, sizeof(MPEG4_OUT));

	return 1;
}

int open_mpeg4out(char *filename, MPEG4_OUT *p, AVISTREAMINFO *avi_format, BITMAPINFOHEADER *frame_format, options *opt)
{
	int n;
	FPS fps;

	fps.rate = avi_format->dwRate;
	fps.scale = avi_format->dwScale;

	initialize_mpeg4out(p);

	p->avi_format = *avi_format;
	p->avi_format.fccHandler = opt->version;

	p->original_format = copy_bitmap_info(frame_format);
	p->compress_format = copy_bitmap_info(frame_format);

	p->compress_format->bmiHeader.biCompression = opt->version;

	if(! open_compressor(&(p->encoder), &(p->original_format->bmiHeader), opt->version, opt->rate, opt->key, opt->cc, &fps) ){
		return 0;
	}

	DeleteFile(filename);
	
	if(AVIFileOpen(&(p->file), filename, OF_CREATE|OF_WRITE|OF_SHARE_EXCLUSIVE, NULL)){
		close_compressor(&(p->encoder));
		return 0;
	}

	p->expand_frame_rate = EXPAND_FRAME_RATE_NO;
	if(opt->expand_frame_rate){
		n = (fps.rate+fps.scale-1)/fps.scale;
		if( (n>23) && (n<25) ){
			p->expand_frame_rate = EXPAND_FRAME_RATE_24_to_60;
			p->avi_format.dwRate *= 5;
			p->avi_format.dwScale *= 2;
		}else if( (n>29) && (n<31) ){
			p->expand_frame_rate = EXPAND_FRAME_RATE_30_to_60;
			p->avi_format.dwRate *= 2;
		}
	}
		
	AVIFileCreateStream(p->file, &(p->video), &(p->avi_format));
	AVIStreamSetFormat(p->video, 0, &(p->compress_format->bmiHeader), p->compress_format->bmiHeader.biSize);

	p->decoder = ICOpen(ICTYPE_VIDEO, p->avi_format.fccHandler, ICMODE_DECOMPRESS);
	ICDecompressBegin(p->decoder, p->compress_format, p->original_format);

	p->decoded_data = malloc(frame_format->biSizeImage);
	p->encoded_data = malloc(frame_format->biSizeImage);
	
	p->delta_frame_max_size = (frame_format->biWidth * frame_format->biHeight / 64) * opt->scene_change_detector;

	return 1;
}

LPBITMAPINFO copy_bitmap_info(LPBITMAPINFOHEADER in)
{
	LPBITMAPINFO r;

	r = malloc(in->biSize+in->biSizeImage);
	memcpy(r,in, in->biSize);

	return r;
}

void compress_force_key_frame(MPEG4_OUT *p, LPVOID data)
{
	start_compression(&(p->encoder));
	compress(data, p->encoded_data, &(p->encoder), &(p->key), &(p->size));
}

int close_mpeg4out(MPEG4_OUT *p)
{
	if(p->video){
		AVIStreamRelease(p->video);
	}
	if(p->file){
		AVIFileRelease(p->file);
	}

	if(p->original_format){
		free(p->original_format);
		p->original_format = NULL;
	}

	if(p->compress_format){
		free(p->compress_format);
		p->compress_format = NULL;
	}

	if(p->encoded_data){
		free(p->encoded_data);
		p->encoded_data = 0;
	}
	
	if(p->encoder.driver){
		close_compressor(&(p->encoder));
	}

	if(p->decoded_data){
		free(p->decoded_data);
		p->decoded_data = NULL;
	}

	if(p->decoder){
		ICDecompressEnd(p->decoder);
		ICClose(p->decoder);
	}

	return 1;
}

void print_time(time_t time, FILE *out)
{
	int dd;
	int hh;
	int mm;
	int ss;

	ss = time % 60;
	mm = time / 60 % 60;
	hh = time / 60 / 60 % 24;
	dd = time / 60 / 60 / 24;

	if(dd){
		fprintf(out, "%02d:%02d:%02d", dd, hh, mm);
	}else{
		fprintf(out, "%02d:%02d:%02d", hh, mm, ss);
	}
}
		
time_t calc_rest_time(int current, int all, time_t start)
{
	time_t now;
	time_t elapsed;
	time_t rest;
	
	now = time(NULL);
	elapsed = now - start;

	if(current == 0){
		current = 1;
	}
	rest = (all - current) * elapsed / current;

	return rest;
}

void print_size(size_t bytes, FILE *out)
{
	size_t g,m,k,b;

	b = bytes % 1024;
	k = bytes / 1024 % 1024;
	m = bytes / 1024 / 1024 % 1024;
	g = bytes / 1024 / 1024 / 1024;

	if(g){
		fprintf(out, "%d.%03d G", g, m * 1000 / 1024);
	}else if(m){
		if(m < 10){
			fprintf(out, "%1d.%03d M", m, k * 1000 / 1024);
		}else if(m < 100){
			fprintf(out, "%2d.%02d M", m, k * 100 / 1024);
		}else if(m < 1000){
			fprintf(out, "%3d.%01d M", m, k * 10 / 1024);
		}else{
			fprintf(out, "%4d. M", m);
		}			
	}else if(k){
		if(k < 10){
			fprintf(out, "%1d.%03d K", k, b * 1000 / 1024);
		}else if(k < 100){
			fprintf(out, "%2d.%02d K", k, b * 100 / 1024);
		}else if(k < 1000){
			fprintf(out, "%3d.%01d K", k, b * 10 / 1024);
		}else{
			fprintf(out, "%4d. K", k);
		}			
	}else{
		fprintf(out, "%4d   ", b);
	}

}

void bad_block_report(LPVOID original, LPVOID result, BITMAPINFOHEADER *format, int x, int y, int diff, int pixel, int frame)
{
	int i,j;
	int d,h;
	unsigned char *in, *out, *p1, *p2;
	
	IMAGE *img;

	int da[256];

	char path[FILENAME_MAX];

	static COLOR white = {
		255, 255, 255,
	};

	static COLOR gray = {
		128, 128, 128,
	};

	static COLOR red = {
		0, 0, 255,
	};

	static COLOR blue = {
		255, 0, 0,
	};

	img = new_image(372, 160);
	fill_rectangle(img, 0, 0, 372, 160, &white);

	/* 2x copy original block */

	in = (unsigned char *)original;
	in += format->biWidth * 3 * (y-8) + (x-8) * 3;

	out = img->data + 372 * (24+48+16) * 3 + 26 * 3;
	
	for(i=0;i<24;i++){
		for(j=0;j<24;j++){
			if( (y-8+i < 0) || (y-8+i >= format->biHeight) || (x-8+j < 0) || (x-8+j >= format->biWidth) ){
				out[j*6+0] = 128;
				out[j*6+1] = 128;
				out[j*6+2] = 128;
				out[j*6+3] = 128;
				out[j*6+4] = 128;
				out[j*6+5] = 128;
				out[372*3+j*6+0] = 128;
				out[372*3+j*6+1] = 128;
				out[372*3+j*6+2] = 128;
				out[372*3+j*6+3] = 128;
				out[372*3+j*6+4] = 128;
				out[372*3+j*6+5] = 128;
			}else{
				out[j*6+0] = in[j*3+0];
				out[j*6+1] = in[j*3+1];
				out[j*6+2] = in[j*3+2];
				out[j*6+3] = in[j*3+0];
				out[j*6+4] = in[j*3+1];
				out[j*6+5] = in[j*3+2];
				out[372*3+j*6+0] = in[j*3+0];
				out[372*3+j*6+1] = in[j*3+1];
				out[372*3+j*6+2] = in[j*3+2];
				out[372*3+j*6+3] = in[j*3+0];
				out[372*3+j*6+4] = in[j*3+1];
				out[372*3+j*6+5] = in[j*3+2];
			}
		}
		out += 372*6;
		in += format->biWidth * 3;
	}

	/* copy bad block */

	in = (unsigned char *)result;
	in += format->biWidth * 3 * (y-8) + (x-8) * 3;

	out = img->data + 372 * 24 * 3 + 26 * 3;
	
	for(i=0;i<24;i++){
		for(j=0;j<24;j++){
			if( (y-8+i < 0) || (y-8+i >= format->biHeight) || (x-8+j < 0) || (x-8+j >= format->biWidth) ){
				out[j*6+0] = 128;
				out[j*6+1] = 128;
				out[j*6+2] = 128;
				out[j*6+3] = 128;
				out[j*6+4] = 128;
				out[j*6+5] = 128;
				out[372*3+j*6+0] = 128;
				out[372*3+j*6+1] = 128;
				out[372*3+j*6+2] = 128;
				out[372*3+j*6+3] = 128;
				out[372*3+j*6+4] = 128;
				out[372*3+j*6+5] = 128;
			}else{
				out[j*6+0] = in[j*3+0];
				out[j*6+1] = in[j*3+1];
				out[j*6+2] = in[j*3+2];
				out[j*6+3] = in[j*3+0];
				out[j*6+4] = in[j*3+1];
				out[j*6+5] = in[j*3+2];
				out[372*3+j*6+0] = in[j*3+0];
				out[372*3+j*6+1] = in[j*3+1];
				out[372*3+j*6+2] = in[j*3+2];
				out[372*3+j*6+3] = in[j*3+0];
				out[372*3+j*6+4] = in[j*3+1];
				out[372*3+j*6+5] = in[j*3+2];
			}
		}
		out += 372*6;
		in += format->biWidth * 3;
	}

	/* analyze bad block */
	
	p1 = (unsigned char *)original;
	p2 = (unsigned char *)result;

	p1 += format->biWidth * 3 * y + x * 3;
	p2 += format->biWidth * 3 * y + x * 3;

	memset(da, 0, 256*sizeof(int));

	for(i=0;i<8;i++){
		for(j=0;j<8;j++){
			d = RGB_to_Y(p1+j*3) - RGB_to_Y(p2+j*3);
			d = abs(d);
			da[d] += 1;
		}
		p1 += format->biWidth * 3;
		p2 += format->biWidth * 3;
	}

	/* create graph */

	fill_rectangle(img, 100, 16, 256, 128, &gray);

	h = 0;
	for(i=255;i>63;i--){
		h += da[i];
	}

	for(i=63;i>=0;i--){
		h += da[i];
		fill_rectangle(img, 100+i*4, 16, 4, h*2, &blue);
	}

	draw_horizontal_line(img, 100, 16+pixel*2, 256, &red);
	draw_vertical_line(img, 100+diff*4, 16, 128, &red);

	/* draw scale */
	for(i=1;i<64;i++){
		if(i%10){
			draw_vertical_line(img, 100+i*4, 16, 3, &white);
		}else{
			draw_vertical_line(img, 100+i*4, 16, 6, &white);
		}
	}

	for(i=1;i<32;i++){
		if(i%5){
			draw_horizontal_line(img, 100, 16+i*4, 3, &white);
		}else{
			draw_horizontal_line(img, 100, 16+i*4, 6, &white);
		}
	}
	
	sprintf(path, "%08d.bmp", frame);

	store_bmp(img, path);

	delete_image(img);
}

/*******************************************************************
              global variables for SIGINT handling
 *******************************************************************/
VIDEO_INPUT   in;
MPEG4_OUT     out;
/*******************************************************************
              global variables for SIGINT handling
 *******************************************************************/

void m4c(char *infile, char *outfile, options *opt)
{
	unsigned int i,j,w;
	int is_vfapi;

	VFAPI      plugin;
	AVI_IN     avi_in;

	LPBITMAPINFOHEADER format;
	LPVOID             data;

	FILE *log;
	char *logfile;

	time_t start;
	time_t rest;

	size_t sum;
	size_t total;

	if(check_sfx(infile, ".avi")){
		if(! open_file_by_vfw(infile, &avi_in) ){
			fprintf(stderr, "Error - Can't open %s\n", infile);
			exit(EXIT_FAILURE);
		}
		in.this = (void *)(& avi_in);
		in.get_frame = read_frame_vfw;
		in.release = close_avi;
		in.max_frame = avi_in.avi_format.dwLength;
		is_vfapi = 0;
	}else{
		if(! open_file_by_vfapi(infile, &plugin) ){
			fprintf(stderr, "Error - Can't open %s\n", infile);
			exit(EXIT_FAILURE);
		}
		in.this = (void *)(& plugin);
		in.get_frame = read_frame_vfapi;
		in.release = close_plugin;
		if(plugin.video_info.dwLengthH){
			in.max_frame = INT_MAX;
		}else{
			in.max_frame = plugin.video_info.dwLengthL;
		}
		is_vfapi = 1;
	}

	if(is_vfapi){
		w = open_mpeg4out(outfile, &out, &(plugin.avi_format), plugin.frame_format, opt);
	}else{
		w = open_mpeg4out(outfile, &out, &(avi_in.avi_format), avi_in.frame_format, opt);
	}

	if(! w){
		fprintf(stderr, "ERROR - Can't open %s\n", outfile);
		in.release(in.this);
		exit(EXIT_FAILURE);
	}

	if(opt->loging){
		logfile = change_sfx(outfile, ".log");
		log = fopen(logfile, "w");
		fprintf(log, "[%s]\n", outfile);
		fprintf(log, "rate: %d, key: %d, cc: %d, pixel: %d, diff: %d\n", opt->rate, opt->key, opt->cc, opt->pixel, opt->diff);
		fprintf(log, LOG_HEADER);
	}

	start = time(NULL);
	sum = 0;

	signal(SIGINT, sigint_handler);
	
	for(i=0,j=0;i<in.max_frame;i++){

		int oversize;
		int address;

		_clearfp();
		
		if(! in.get_frame(in.this, &data, &format) ){
			break;
		}

		if(! compress(data, out.encoded_data, &(out.encoder), &(out.key), &(out.size)) ){
			break;
		}

		if(!(out.key)){
			if(out.delta_frame_max_size && (out.size > out.delta_frame_max_size) ){
				oversize = out.size;
				compress_force_key_frame(&out, data);
				if(opt->loging){
					fprintf(log, LOG_FORMAT_SCENE_CHANGE, i, out.size, "***", oversize);
				}
			}else{
				ICDecompress(out.decoder, ICDECOMPRESS_NOTKEYFRAME, &(out.compress_format->bmiHeader), out.encoded_data, format, out.decoded_data);
				address = check_block(data, out.decoded_data, format, opt->diff, opt->pixel); 
				if(address){
					compress_force_key_frame(&out, data);
					if(opt->loging){
						fprintf(log, LOG_FORMAT_BAD_BLOCK, i, out.size, "***", (address >> 2) & 0xFFFF, format->biHeight - (address >> 18) - 8);
					}
					if(opt->bad_block_report){
						bad_block_report(data, out.decoded_data, format, (address >> 2) & 0xFFFF, (address >> 18), opt->diff, opt->pixel, i);
					}
				}else{
					if(opt->loging){
						fprintf(log, LOG_FORMAT, i, out.size, "");
					}
				}
			}
		}else{
			if(opt->loging){
				fprintf(log, LOG_FORMAT, i, out.size, "***");
			}
		}
		
		if(out.key){
			ICDecompressBegin(out.decoder, out.compress_format, out.original_format);
			ICDecompress(out.decoder, 0, &(out.compress_format->bmiHeader), out.encoded_data, format, out.decoded_data);
		}
		
		AVIStreamWrite(out.video, j, 1, out.encoded_data, out.size, out.key ? AVIIF_KEYFRAME : 0, NULL, NULL);
		j+=1;
		switch(out.expand_frame_rate){
		case EXPAND_FRAME_RATE_24_to_60:
			if(i&1){
				AVIStreamWrite(out.video, j, 1, NULL, 0, 0, NULL, NULL);
				j+=1;
				AVIStreamWrite(out.video, j, 1, NULL, 0, 0, NULL, NULL);
				j+=1;
			}else{
				AVIStreamWrite(out.video, j, 1, NULL, 0, 0, NULL, NULL);
				j+=1;
			}				
			break;
		case EXPAND_FRAME_RATE_30_to_60:
			AVIStreamWrite(out.video, j, 1, NULL, 0, 0, NULL, NULL);
			j+=1;
			break;
		}
		
		if(opt->verbose){
			fprintf(stderr, "\rframe: %6d/%6d, rest time: ", i+1,in.max_frame);

			rest = calc_rest_time(i+1, in.max_frame, start);
			print_time(rest, stderr);

			fprintf(stderr, ", estimate size: ");
			
			sum += out.size;
			total = sum / (i+1) * in.max_frame;
			print_size(total, stderr);
			
			fflush(stderr);
		}
	}
	if(opt->loging){
		fclose(log);
		free(logfile);
	}
	close_mpeg4out(&out);
	in.release(in.this);

	signal(SIGINT, SIG_DFL);
	
	fprintf(stderr, "\nfinish!! total time: ");
	print_time(time(NULL) - start, stderr);
	fprintf(stderr, "\n");
}

void _cdecl sigint_handler(int sig)
{
	close_mpeg4out(&out);
	in.release(in.this);
	
	exit(EXIT_FAILURE);
}
