/*******************************************************************
                    MPEG-2 VIDEO module
 *******************************************************************/
#include <windows.h>
#include <winreg.h>
#include <stdio.h>

#include "gop_list.h"
#include "filename.h"
#include "idct_int32.h"
#include "idct_mmx32.h"
#include "idct_double.h"
#include "idct_sse32.h"

#define MPEG2VIDEO_C
#include "mpeg2video.h"

/* public */
int open_mpeg2video(char *path, MPEG2VIDEO *out);
int close_mpeg2video(MPEG2VIDEO *p);
int read_frame(MPEG2VIDEO *in, FRAME **out, __int64 frame);

/* local */
static int get_color_conversion_type();
static int get_simd_mode();
static int get_field_mode();
static int get_resize_mode();
static void select_idct_function(DECODE_PICTURE_PARAMETER *p);

static int is_frame_in_current_gop(MPEG2VIDEO *p, __int64 frame);

static void sequence_header_to_decode_picture_parameter(SEQUENCE_HEADER *in, DECODE_PICTURE_PARAMETER *out);
static void picture_header_to_decode_picture_parameter(PICTURE_HEADER *in, DECODE_PICTURE_PARAMETER *out);

static void decode_2nd_field(MPEG2VIDEO *p);

static void delete_data_out_buffer(OUT_BUFFER *p);
static void add_data_out_buffer(OUT_BUFFER *p, FRAME *data, __int64 index, int i_frame);
static OUT_BUFFER_ELEMENT *search_out_buffer(OUT_BUFFER *p, __int64 index);

static void delete_frame_field_buffer(FIELD_BUFFER *p);
static void add_frame_field_buffer(FIELD_BUFFER *p, FRAME *data, __int64 index, int i_frame, int field_status);
static FRAME *store_frame_top_field_first(FIELD_BUFFER *in, OUT_BUFFER *out, __int64 index);
static FRAME *store_frame_bottom_field_first(FIELD_BUFFER *in, OUT_BUFFER *out, __int64 index);
static FRAME *store_frame_keep_frame(FIELD_BUFFER *in, OUT_BUFFER *out, __int64 index);

static int get_field_status(OUTPUT_PARAMETER *prm, int field_order);

/******************************************************************************/
int open_mpeg2video(char *path, MPEG2VIDEO *out)
{
	int field_order_unmatch;
	int simd;
	int code;
	
	GOP g;
	READ_GOP_PARAMETER *gp;
	GOP_LIST *gl;

	char gl_path[FILENAME_MAX]; /* GOP LIST file path */

	/* initialize */
	memset(out, 0, sizeof(MPEG2VIDEO));

	if(!open_video_stream(path, &(out->bitstream))){
		/* can't open */
		return 0;
	}

	if(!vs_next_start_code(&(out->bitstream))){
		/* not MPEG-2 VIDEO file */
		close_video_stream(&(out->bitstream));
		return 0;
	}

	code = vs_get_bits(&(out->bitstream), 32);
	if(code != 0x1B3){
		/* not VIDEO stream file (Program Stream or Transport Stream) */
		close_video_stream(&(out->bitstream));
		return 0;
	}

	if(!read_sequence_header(&(out->bitstream), &(out->seq))){
		/* has invalid sequence header */
		close_video_stream(&(out->bitstream));
		return 0;
	}

	if(! out->seq.has_sequence_extension){
		/* not MPEG-2 VIDEO stream */
		close_video_stream(&(out->bitstream));
		return 0;
	}

	out->remap = get_color_conversion_type();
	sequence_header_to_bgr_conversion_parameter(&(out->seq), &(out->bgr_prm), out->remap);

	out->fwd_prm.index = -1;
	out->bwd_prm.index = -1;

	sequence_header_to_read_picture_header_option(&(out->seq), &(out->pic_opt));

	sequence_header_to_decode_picture_parameter(&(out->seq), &(out->dec_prm));

	out->dec_buf.forward = NULL;
	out->dec_buf.backward = NULL;
	out->dec_buf.current = NULL;

	while(vs_next_start_code(&(out->bitstream))){
		if(vs_get_bits(&(out->bitstream, 32) == 0x100)){
			read_picture_header(&(out->bitstream), &(out->pic), &(out->pic_opt));
			if(out->pic.has_picture_coding_extension){
				out->field_order = out->pic.pc.top_field_first;
			}else{
				out->field_order = TOP_FIELD_FIRST;
			}
			video_stream_seek(&(out->bitstream), 0, SEEK_SET);
			break;
		}
	}

	field_order_unmatch = 0;
	switch(get_field_mode()){
	case 0:
		out->fld2out = store_frame_keep_frame;
		break;
	case 1:
		out->fld2out = store_frame_top_field_first;
		if(out->field_order != TOP_FIELD_FIRST){
			out->field_order = TOP_FIELD_FIRST;
			field_order_unmatch = 1;
		}
		break;
	case 2:
		out->fld2out = store_frame_bottom_field_first;
		if(out->field_order != BOTTOM_FIELD_FIRST){
			out->field_order = BOTTOM_FIELD_FIRST;
			field_order_unmatch = 1;
		}
		break;
	default:
		out->fld2out = store_frame_keep_frame;
	}

	gp = new_read_gop_parameter(&(out->bitstream), &(out->seq), &(out->pic_opt), out->field_order);
	if(gp == NULL){
		/* malloc failed */
		close_video_stream(&(out->bitstream));
		return 0;
	}

	out->rate = gp->rate;
	out->scale = gp->scale;

	if(read_gop(gp, &g)){
		/* gop timecode is sequential */
		out->fg.arg1 = (void *)gp;
		out->fg.func = find_gop_with_timecode;
		out->fg.release = delete_read_gop_parameter;
		
		out->total = count_frame(gp);
	}

	if(out->total <= 0){
		/* gop timecode is not sequential */
		delete_read_gop_parameter(gp);
		
		strcpy(gl_path, path);
		cut_suffix(gl_path);
		strcat(gl_path, ".gl");

		gl = load_gop_list(gl_path);
		if( (gl == NULL) || (gl->stream_length != out->bitstream.file_length) ){
			video_stream_seek(&(out->bitstream), 0, SEEK_SET);
			gl = new_gop_list(&(out->bitstream), &(out->pic_opt), out->field_order);
			if(gl == NULL){
				/* stream has something probrem */
				close_video_stream(&(out->bitstream));
				return 0;
			}
			store_gop_list(gl, gl_path);
		}
		
		out->fg.arg1 = (void *) gl;
		out->fg.func = find_gop_with_gop_list;
		out->fg.release = delete_gop_list;

		out->total = gl->num_of_frame;
	}

	out->total -= field_order_unmatch;
	
	video_stream_seek(&(out->bitstream), 0, SEEK_SET);

	out->current.frame_count = 0;
	out->current.start_frame = 0;

	select_idct_function(&(out->dec_prm));

	out->resize = get_resize_mode();

	simd = get_simd_mode();
	if(simd & 1){
		out->frm2bgr = yuv444_to_bgr_mmx;
		out->upsmp_cr = upsample_chroma_mmx;
		out->dec_prm.add_block_func = add_block_data_to_frame_mmx;
		out->dec_prm.mc_parameter.prediction_func = prediction_mmx;
	}else{
		out->frm2bgr = yuv444_to_bgr;
		out->upsmp_cr = upsample_chroma;
		out->dec_prm.add_block_func = add_block_data_to_frame;
		out->dec_prm.mc_parameter.prediction_func = prediction;
	}

	InitializeCriticalSection(&(out->lock));

	return 1;
}
	
int close_mpeg2video(MPEG2VIDEO *p)
{
	if(p == NULL){
		return 0;
	}

	while(p->out_buf.head){
		delete_data_out_buffer(&(p->out_buf));
	}

	if(p->fg.release){
		p->fg.release(p->fg.arg1);
	}

	close_video_stream(&(p->bitstream));

	DeleteCriticalSection(&(p->lock));
	
	return 1;
}

int read_frame(MPEG2VIDEO *in, FRAME **out, __int64 frame)
{
	int code;
	
	OUT_BUFFER_ELEMENT *w;

	int field_status;
	FRAME *rff_buffer;

	EnterCriticalSection(&(in->lock));
	

	w = search_out_buffer(&(in->out_buf), frame);
	if(w){
		*out = w->data;
		goto READ_FRAME_NORMAL_END;
	}

	if(!is_frame_in_current_gop(in, frame)){
		in->current = in->fg.func(in->fg.arg1, frame);
		video_stream_seek(&(in->bitstream), in->current.offset, SEEK_SET);
		if(in->dec_buf.forward){
			delete_frame(in->dec_buf.forward);
			in->dec_buf.forward = NULL;
		}
		if(in->dec_buf.backward){
			delete_frame(in->dec_buf.backward);
			in->dec_buf.backward = NULL;
		}
		in->fwd_prm.index = in->current.start_frame - 1;
		in->bwd_prm.index = in->current.start_frame - 1;
		in->fwd_prm.i_frame = 0;
		in->bwd_prm.i_frame = 0;
		while(in->fld_buf.head){
			delete_frame_field_buffer(&(in->fld_buf));
		}
		while(in->out_buf.head){
			delete_data_out_buffer(&(in->out_buf));
		}
		if(in->current.frame_count == 0){
			goto READ_FRAME_ABNORMAL_END;
		}
		if(in->current.start_frame > frame){
			goto READ_FRAME_ABNORMAL_END;
		}
	}
		
	*out = NULL;
	while(vs_next_start_code(&(in->bitstream))){
		code = vs_get_bits(&(in->bitstream), 32);

		if(code == 0x1B3){ /* sequence */
			
			read_sequence_header(&(in->bitstream), &(in->seq));
			
			sequence_header_to_bgr_conversion_parameter(&(in->seq), &(in->bgr_prm), in->remap);

			sequence_header_to_read_picture_header_option(&(in->seq), &(in->pic_opt));

			sequence_header_to_decode_picture_parameter(&(in->seq), &(in->dec_prm));
			
		}else if(code == 0x1B8){ /* GOP */
			
			vs_erase_bits(&(in->bitstream), 26);
			if(vs_get_bits(&(in->bitstream), 1)){ /* broken link */

				if(in->dec_buf.forward){
					field_status = get_field_status(&(in->fwd_prm), in->field_order);
					in->upsmp_cr(in->dec_buf.forward);
					add_frame_field_buffer(&(in->fld_buf), in->dec_buf.forward, in->fwd_prm.index, in->fwd_prm.i_frame, field_status);
					in->dec_buf.forward = NULL;
				}
	
				if(in->dec_buf.backward){
					field_status = get_field_status(&(in->bwd_prm), in->field_order);
					in->upsmp_cr(in->dec_buf.backward);
					add_frame_field_buffer(&(in->fld_buf), in->dec_buf.backward, in->bwd_prm.index, in->bwd_prm.i_frame, field_status);
					in->dec_buf.backward = NULL;
				}

				*out = in->fld2out(&(in->fld_buf), &(in->out_buf), frame);

				if(*out){
					goto READ_FRAME_NORMAL_END;
				}
			}
			
		}else if(code == 0x100){ /* picture */

			read_picture_header(&(in->bitstream), &(in->pic), &(in->pic_opt));
			
			picture_header_to_decode_picture_parameter(&(in->pic), &(in->dec_prm));
			picture_header_to_bgr_conversion_parameter(&(in->pic), &(in->bgr_prm));

			in->dec_buf.current = new_frame(in->seq.h_size, in->seq.v_size, &(in->bgr_prm));
			in->cur_prm.index = in->bwd_prm.index;
			in->cur_prm.i_frame = 0;

			if(in->pic.has_picture_coding_extension){
				in->cur_prm.repeat_first_field = in->pic.pc.repeat_first_field;
				in->cur_prm.top_field_first = in->pic.pc.top_field_first;
			}else{
				in->cur_prm.repeat_first_field = 0;
				in->cur_prm.top_field_first = 0;
			}

			switch(in->pic.picture_coding_type){
			case 1:
				in->cur_prm.i_frame = 1;
			case 2:
				if(in->dec_buf.forward){
					field_status = get_field_status(&(in->fwd_prm), in->field_order);
					in->upsmp_cr(in->dec_buf.forward);
					add_frame_field_buffer(&(in->fld_buf), in->dec_buf.forward, in->fwd_prm.index, in->fwd_prm.i_frame, field_status);
					*out = in->fld2out(&(in->fld_buf), &(in->out_buf), frame);
				}
				in->dec_buf.forward = in->dec_buf.backward;
				in->dec_buf.backward = in->dec_buf.current;
				in->fwd_prm = in->bwd_prm;
				in->bwd_prm = in->cur_prm;
				in->bwd_prm.index += 1;

				if(in->fwd_prm.i_frame && (in->fwd_prm.index != in->current.start_frame) ){
					in->current.frame_count = in->fwd_prm.index - in->current.start_frame;
					in->current.start_frame = in->fwd_prm.index;
				}

				if(in->fwd_prm.repeat_first_field && (in->fwd_prm.top_field_first == in->field_order) ){
					rff_buffer = new_frame(in->seq.h_size, in->seq.v_size, &(in->bgr_prm));
					copy_frame(in->dec_buf.forward, rff_buffer);
					in->upsmp_cr(rff_buffer);
					add_frame_field_buffer(&(in->fld_buf), rff_buffer, in->fwd_prm.index, in->fwd_prm.i_frame, in->fwd_prm.top_field_first);
					if(in->fwd_prm.index == frame){
						*out = rff_buffer;
					}
					in->fwd_prm.index += 1;
					in->bwd_prm.index += 1;
				}
			}

			if((in->pic.picture_coding_type == 3 ) && ( (in->dec_buf.forward == NULL) || (in->dec_buf.backward == NULL) )){
				delete_frame(in->dec_buf.current);
				in->dec_prm.mc_parameter.first_field = 0;
				continue;
			}else if((in->pic.picture_coding_type == 2) && (in->dec_buf.forward == NULL)){
				delete_frame(in->dec_buf.current);
				in->dec_prm.mc_parameter.first_field = 0;
				continue;
			}

			decode_picture(&(in->bitstream), &(in->dec_buf), &(in->dec_prm));

			if(in->pic.has_picture_coding_extension && in->pic.pc.picture_structure != 3){
				decode_2nd_field(in);
			}

			if(in->pic.picture_coding_type == 3){

				in->upsmp_cr(in->dec_buf.current);
				
				if(in->cur_prm.repeat_first_field){
					if(in->cur_prm.top_field_first == in->field_order){
						rff_buffer = new_frame(in->seq.h_size, in->seq.v_size, &(in->bgr_prm));
						copy_frame(in->dec_buf.current, rff_buffer);
						add_frame_field_buffer(&(in->fld_buf), rff_buffer, in->cur_prm.index, in->cur_prm.i_frame, in->cur_prm.top_field_first);

						in->cur_prm.index += 1;
						in->bwd_prm.index += 1;
						field_status = REST_FIELD;
					}else{
						field_status = REPEAT_FIELD;
					}
				}else{
					field_status = in->cur_prm.top_field_first;
				}

				add_frame_field_buffer(&(in->fld_buf), in->dec_buf.current, in->cur_prm.index, 0, field_status);
				
				in->bwd_prm.index += 1;
			}

			if(*out){
				goto READ_FRAME_NORMAL_END;
			}
		}
	}
				
	if(in->dec_buf.forward){
		field_status = get_field_status(&(in->fwd_prm), in->field_order);
		in->upsmp_cr(in->dec_buf.forward);
		add_frame_field_buffer(&(in->fld_buf), in->dec_buf.forward, in->fwd_prm.index, in->fwd_prm.i_frame, field_status);
		in->dec_buf.forward = NULL;
	}
	
	if(in->dec_buf.backward){
		field_status = get_field_status(&(in->bwd_prm), in->field_order);
		in->upsmp_cr(in->dec_buf.backward);
		add_frame_field_buffer(&(in->fld_buf), in->dec_buf.backward, in->bwd_prm.index, in->bwd_prm.i_frame, field_status);
		in->dec_buf.backward = NULL;
	}

	*out = in->fld2out(&(in->fld_buf), &(in->out_buf), frame);

	in->fwd_prm.index = -1;
	in->bwd_prm.index = -1;

	if(*out){
		goto READ_FRAME_NORMAL_END;
	}

READ_FRAME_ABNORMAL_END:
	LeaveCriticalSection(&(in->lock));
	return 0;
	
READ_FRAME_NORMAL_END:
	LeaveCriticalSection(&(in->lock));
	return 1;
}

static int get_color_conversion_type()
{
	HKEY key;
	DWORD type;
	DWORD size;
	DWORD value;

	if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\marumo\\mpeg2vid_vfp", 0, KEY_READ, &key) != ERROR_SUCCESS){
		return 1;
	}

	type = REG_DWORD;
	size = sizeof(DWORD);
	
	if(RegQueryValueEx(key, "re_map", NULL, &type, (LPBYTE)&value, &size) != ERROR_SUCCESS){
		RegCloseKey(key);
		return 1;
	}

	RegCloseKey(key);

	if(value){
		return 1;
	}

	return 0;
}

static void select_idct_function(DECODE_PICTURE_PARAMETER *p)
{
	HKEY key;
	DWORD type;
	DWORD size;
	DWORD value;
	DWORD simd;

	if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\marumo\\mpeg2vid_vfp", 0, KEY_READ, &key) != ERROR_SUCCESS){
		p->idct_func = idct_int32;
		return;
	}

	type = REG_DWORD;
	size = sizeof(DWORD);
	
	if(RegQueryValueEx(key, "idct_func", NULL, &type, (LPBYTE)&value, &size) != ERROR_SUCCESS){
		RegCloseKey(key);
		p->idct_func = idct_int32;
		return;
	}

	if(RegQueryValueEx(key, "simd", NULL, &type, (LPBYTE)&simd, &size) != ERROR_SUCCESS){
		simd = 0;
	}
	
	RegCloseKey(key);

	switch(value){
	case 0:
		if(simd & 2){
			p->idct_func = idct_sse32;
		}else{
			p->idct_func = idct_double;
		}
		return; 
	case 1:
		if(simd & 1){
			p->idct_func = idct_mmx32;
		}else{
			p->idct_func = idct_int32;
		}
		return;
	default:
		p->idct_func = idct_int32;
	}
	
	return;
}

static int get_simd_mode()
{
	HKEY key;
	DWORD type;
	DWORD size;
	DWORD value;

	if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\marumo\\mpeg2vid_vfp", 0, KEY_READ, &key) != ERROR_SUCCESS){
		return 0;
	}

	type = REG_DWORD;
	size = sizeof(DWORD);
	
	if(RegQueryValueEx(key, "simd", NULL, &type, (LPBYTE)&value, &size) != ERROR_SUCCESS){
		RegCloseKey(key);
		return 0;
	}

	RegCloseKey(key);

	if(value & 1){
		return 1;
	}

	return 0;
}

static int get_field_mode()
{
	HKEY key;
	DWORD type;
	DWORD size;
	DWORD value;

	if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\marumo\\mpeg2vid_vfp", 0, KEY_READ, &key) != ERROR_SUCCESS){
		return 0;
	}

	type = REG_DWORD;
	size = sizeof(DWORD);
	
	if(RegQueryValueEx(key, "field_order", NULL, &type, (LPBYTE)&value, &size) != ERROR_SUCCESS){
		RegCloseKey(key);
		return 0;
	}

	RegCloseKey(key);

	return value;
}

static int get_resize_mode()
{
	HKEY key;
	DWORD type;
	DWORD size;
	DWORD value;

	if(RegOpenKeyEx(HKEY_CURRENT_USER, "Software\\marumo\\mpeg2vid_vfp", 0, KEY_READ, &key) != ERROR_SUCCESS){
		return 0;
	}

	type = REG_DWORD;
	size = sizeof(DWORD);
	
	if(RegQueryValueEx(key, "aspect_ratio", NULL, &type, (LPBYTE)&value, &size) != ERROR_SUCCESS){
		RegCloseKey(key);
		return 0;
	}

	RegCloseKey(key);

	return value;
}

static int is_frame_in_current_gop(MPEG2VIDEO *p, __int64 frame)
{
	__int64 n;
	FIELD_BUFFER_ELEMENT *w;
	
	if(p->fwd_prm.index == frame){
		return 1;
	}

	if(p->bwd_prm.index == frame){
		return 1;
	}

	w = p->fld_buf.head;
	while(w){
		if(w->index == frame){
			return 1;
		}
		w = w->next;
	}
		
	n = p->current.start_frame + p->current.frame_count;
	if(frame < n){
		if(frame < p->current.start_frame){
			return 0;
		}
		return 1;
	}
	
	return 0;
}

static void sequence_header_to_decode_picture_parameter(SEQUENCE_HEADER *in, DECODE_PICTURE_PARAMETER *out)
{
	sequence_header_to_read_slice_header_option(in, &(out->slice_option));
	sequence_header_to_read_macroblock_option(in, &(out->macroblock_option));
	sequence_header_to_read_block_option(in, &(out->block_option));
	sequence_header_to_mc_parameter(in, &(out->mc_parameter));
}

static void picture_header_to_decode_picture_parameter(PICTURE_HEADER *in, DECODE_PICTURE_PARAMETER *out)
{
	picture_header_to_read_macroblock_option(in, &(out->macroblock_option));
	picture_header_to_read_block_option(in, &(out->block_option));
	picture_header_to_mc_parameter(in, &(out->mc_parameter));
}

static void decode_2nd_field(MPEG2VIDEO *p)
{
	int code;

	__int64 offset;
	
	while(vs_next_start_code(&(p->bitstream))){
		code = vs_read_bits(&(p->bitstream), 32);
		if(code == 0x100){
			offset = video_stream_tell(&(p->bitstream));
			vs_erase_bits(&(p->bitstream), 32);
			
			read_picture_header(&(p->bitstream), &(p->pic), &(p->pic_opt));
			
			if(p->pic.has_picture_coding_extension && (p->pic.pc.picture_structure !=3) ){
				picture_header_to_decode_picture_parameter(&(p->pic), &(p->dec_prm));
				
				decode_picture(&(p->bitstream), &(p->dec_buf), &(p->dec_prm));

			}else{
				video_stream_seek(&(p->bitstream), offset, SEEK_SET);
			}

			return;
		}else{
			vs_erase_bits(&(p->bitstream), 32);
		}
	}
}

static void delete_data_out_buffer(OUT_BUFFER *p)
{
	OUT_BUFFER_ELEMENT *w;

	w = p->head->next;

	if(p->head->i_frame){
		p->i_frame_count -= 1;
	}
	
	if(p->head->data){
		delete_frame(p->head->data);
	}
	
	free(p->head);

	if(w){
		p->head = w;
		p->head->prev = NULL;
	}else{
		p->head = NULL;
		p->tail = NULL;
	}
}

static void add_fbe_out_buffer(OUT_BUFFER *p, FIELD_BUFFER_ELEMENT *fbe)
{
	OUT_BUFFER_ELEMENT *w;

	w = (OUT_BUFFER_ELEMENT *)malloc(sizeof(OUT_BUFFER_ELEMENT));
	w->index = fbe->index;
	w->i_frame = fbe->i_frame;
	w->data = fbe->data;
	w->prev = p->tail;
	w->next = NULL;

	if(p->head == NULL){
		p->head = w;
		p->tail = w;
	}else{
		p->tail->next = w;
		p->tail = w;
	}
	
	if(fbe->i_frame){
		p->i_frame_count += 1;
	}

	if( (p->i_frame_count > 1) || (p->i_frame_count && !(p->head->i_frame) ) ){
		delete_data_out_buffer(p);
	}
}

static OUT_BUFFER_ELEMENT *search_out_buffer(OUT_BUFFER *p, __int64 index)
{
	OUT_BUFFER_ELEMENT *r;

	r = p->tail;

	while(r){
		if(r->index == index){
			return r;
		}else{
			r = r->prev;
		}
	}

	return NULL;
}

static void delete_frame_field_buffer(FIELD_BUFFER *p)
{
	FIELD_BUFFER_ELEMENT *w;

	w = p->head->next;

	if(p->head->data){
		delete_frame(p->head->data);
	}
	
	free(p->head);

	if(w){
		p->head = w;
		p->head->prev = NULL;
	}else{
		p->head = NULL;
		p->tail = NULL;
	}
}

static void add_frame_field_buffer(FIELD_BUFFER *p, FRAME *data, __int64 index, int i_frame, int field_status)
{
	FIELD_BUFFER_ELEMENT *w, *prev, *next;

	w = (FIELD_BUFFER_ELEMENT *)malloc(sizeof(FIELD_BUFFER_ELEMENT));
	w->index = index;
	w->field_status = field_status;
	w->i_frame = i_frame;
	w->data = data;
	w->prev = NULL;
	w->next = NULL;

	prev = p->tail;
	next = NULL;
	while(prev){
		if(prev->index < index){
			prev->next = w;
			w->prev = prev;
			w->next = next;
			if(next == NULL){
				p->tail = w;
			}else{
				next->prev = w;
			}
			return;
		}else{
			next = prev;
			prev = prev->prev;
		}
	}

	if(p->tail == NULL){
		p->head = w;
		p->tail = w;
	}else{
		w->next = p->head;
		p->head->prev = w;
		p->head = w;
	}
}

static FRAME *store_frame_top_field_first(FIELD_BUFFER *in, OUT_BUFFER *out, __int64 index)
{
	FRAME *r;
	FIELD_BUFFER_ELEMENT *w;

	r = NULL;
	
	while(in->head){
		switch(in->head->field_status){
		case BOTTOM_FIELD_FIRST:
		case REST_FIELD:
			if(in->head->next){
				w = in->head->next;
				
				copy_field(w->data, in->head->data, 1);
				if(in->head->index == index){
					r = in->head->data;
				}
				add_fbe_out_buffer(out, in->head);
				in->head->data = NULL;
				delete_frame_field_buffer(in);

				switch(w->field_status){
				case BOTTOM_FIELD_FIRST:
					w->field_status = REST_FIELD;
					break;
				case TOP_FIELD_FIRST:
				case REST_FIELD:
					/* bug? */
					w->field_status = TOP_FIELD_FIRST;
					break;
				case REPEAT_FIELD:
					w->field_status = TOP_FIELD_FIRST;
					break;
				}
			}else{
				return r;
			}
			break;
		case TOP_FIELD_FIRST:
		case REPEAT_FIELD:
			if(in->head->index == index){
				r = in->head->data;
			}
			add_fbe_out_buffer(out, in->head);

			in->head->data = NULL;
			delete_frame_field_buffer(in);
			
			w = in->head;
			if(w){
				switch(w->field_status){
				case BOTTOM_FIELD_FIRST:
					/* bug? */
					w->field_status = TOP_FIELD_FIRST;
					break;
				case TOP_FIELD_FIRST:
				case REST_FIELD:
					/* nothing todo */
					break;
				case REPEAT_FIELD:
					/* bug? */
					w->field_status = TOP_FIELD_FIRST;
					break;
				}
			}
			break;
		}
	}
	
	return r;
}

static FRAME *store_frame_bottom_field_first(FIELD_BUFFER *in, OUT_BUFFER *out, __int64 index)
{
	FRAME *r;
	FIELD_BUFFER_ELEMENT *w;

	r = NULL;
	
	while(in->head){
		switch(in->head->field_status){
		case TOP_FIELD_FIRST:
		case REST_FIELD:
			if(in->head->next){
				w = in->head->next;
				
				copy_field(w->data, in->head->data, 0);
				if(in->head->index == index){
					r = in->head->data;
				}
				add_fbe_out_buffer(out, in->head);
				in->head->data = NULL;
				delete_frame_field_buffer(in);

				switch(w->field_status){
				case TOP_FIELD_FIRST:
					w->field_status = REST_FIELD;
					break;
				case BOTTOM_FIELD_FIRST:
				case REST_FIELD:
					/* bug? */
					w->field_status = BOTTOM_FIELD_FIRST;
					break;
				case REPEAT_FIELD:
					w->field_status = BOTTOM_FIELD_FIRST;
					break;
				}
			}else{
				return r;
			}
			break;
		case BOTTOM_FIELD_FIRST:
		case REPEAT_FIELD:
			if(in->head->index == index){
				r = in->head->data;
			}
			add_fbe_out_buffer(out, in->head);

			in->head->data = NULL;
			delete_frame_field_buffer(in);
			
			w = in->head;
			if(w){
				switch(w->field_status){
				case TOP_FIELD_FIRST:
					/* bug? */
					w->field_status = BOTTOM_FIELD_FIRST;
					break;
				case BOTTOM_FIELD_FIRST:
				case REST_FIELD:
					/* nothing todo */
					break;
				case REPEAT_FIELD:
					/* bug? */
					w->field_status = BOTTOM_FIELD_FIRST;
					break;
				}
			}
			break;
		}
	}
	
	return r;
}

static FRAME *store_frame_keep_frame(FIELD_BUFFER *in, OUT_BUFFER *out, __int64 index)
{
	FRAME *r;

	r = NULL;
	
	while(in->head){
		if(in->head->index == index){
			r = in->head->data;
		}
		add_fbe_out_buffer(out, in->head);
		in->head->data = NULL;
		delete_frame_field_buffer(in);
	}

	return r;
}

static int get_field_status(OUTPUT_PARAMETER *prm, int field_order)
{
	if(prm->repeat_first_field){
		if(prm->top_field_first != field_order){
			return REPEAT_FIELD;
		}else{
			return REST_FIELD;
		}
	}else{
		return prm->top_field_first;
	}
}
