/* putpic.c, block and motion vector encoding routines                      */

/* Copyright (C) 1996, MPEG Software Simulation Group. All Rights Reserved. */

/*
 * Disclaimer of Warranty
 *
 * These software programs are available to the user without any license fee or
 * royalty on an "as is" basis.  The MPEG Software Simulation Group disclaims
 * any and all warranties, whether express, implied, or statuary, including any
 * implied warranties or merchantability or of fitness for a particular
 * purpose.  In no event shall the copyright-holder be liable for any
 * incidental, punitive, or consequential damages of any kind whatsoever
 * arising from the use of these programs.
 *
 * This disclaimer of warranty extends to the user of these programs and user's
 * customers, employees, agents, transferees, successors, and assigns.
 *
 * The MPEG Software Simulation Group does not represent or warrant that the
 * programs furnished hereunder are free of infringement of any third-party
 * patents.
 *
 * Commercial implementations of MPEG-1 and MPEG-2 video, including shareware,
 * are subject to royalty fees to patent holders.  Many of these patents are
 * general enough such that they are unavoidable regardless of implementation
 * design.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include "config.h"
#include "global.h"

/* output motion vectors (6.2.5.2, 6.3.16.2)
 *
 * this routine also updates the predictions for motion vectors (PMV)
 */
 
static void putmvs(slice_engine_t *engine,
	pict_data_s *picture,
	mbinfo_s *mb,
	int PMV[2][2][2],
	int back)
{
	int hor_f_code;
	int vert_f_code;

	if( back )
	{
		hor_f_code = picture->back_hor_f_code;
		vert_f_code = picture->back_vert_f_code;
	}
	else
	{
		hor_f_code = picture->forw_hor_f_code;
		vert_f_code = picture->forw_vert_f_code;
	}


	if(picture->pict_struct == FRAME_PICTURE)
	{
    	if(mb->motion_type == MC_FRAME)
    	{
/* frame prediction */
    		putmv(engine, mb->MV[0][back][0] - PMV[0][back][0], hor_f_code);
    		putmv(engine, mb->MV[0][back][1] - PMV[0][back][1], vert_f_code);
    		PMV[0][back][0] = PMV[1][back][0] = mb->MV[0][back][0];
    		PMV[0][back][1] = PMV[1][back][1] = mb->MV[0][back][1];
    	}
    	else 
		if (mb->motion_type == MC_FIELD)
    	{
    		/* field prediction */
    		slice_putbits(engine, mb->mv_field_sel[0][back], 1);
    		putmv(engine, mb->MV[0][back][0] - PMV[0][back][0], hor_f_code);
    		putmv(engine, (mb->MV[0][back][1] >> 1) - (PMV[0][back][1] >> 1), vert_f_code);
    		slice_putbits(engine, mb->mv_field_sel[1][back], 1);
    		putmv(engine, mb->MV[1][back][0] - PMV[1][back][0], hor_f_code);
    		putmv(engine, (mb->MV[1][back][1] >> 1) - (PMV[1][back][1] >> 1), vert_f_code);
    		PMV[0][back][0] = mb->MV[0][back][0];
    		PMV[0][back][1] = mb->MV[0][back][1];
    		PMV[1][back][0] = mb->MV[1][back][0];
    		PMV[1][back][1] = mb->MV[1][back][1];
    	}
    	else
    	{
    		/* dual prime prediction */
    		putmv(engine, mb->MV[0][back][0] - PMV[0][back][0], hor_f_code);
    		putdmv(engine, mb->dmvector[0]);
    		putmv(engine, (mb->MV[0][back][1] >> 1) - (PMV[0][back][1] >> 1), vert_f_code);
    		putdmv(engine, mb->dmvector[1]);
    		PMV[0][back][0] = PMV[1][back][0] = mb->MV[0][back][0];
    		PMV[0][back][1] = PMV[1][back][1] = mb->MV[0][back][1];
    	}
	}
	else
	{
    	/* field picture */
    	if(mb->motion_type == MC_FIELD)
    	{
    		/* field prediction */
    		slice_putbits(engine, mb->mv_field_sel[0][back], 1);
    		putmv(engine, mb->MV[0][back][0] - PMV[0][back][0], hor_f_code);
    		putmv(engine, mb->MV[0][back][1] - PMV[0][back][1], vert_f_code);
    		PMV[0][back][0] = PMV[1][back][0] = mb->MV[0][back][0];
    		PMV[0][back][1] = PMV[1][back][1] = mb->MV[0][back][1];
    	}
    	else 
		if(mb->motion_type == MC_16X8)
    	{
    		/* 16x8 prediction */
    		slice_putbits(engine, mb->mv_field_sel[0][back], 1);
    		putmv(engine, mb->MV[0][back][0] - PMV[0][back][0], hor_f_code);
    		putmv(engine, mb->MV[0][back][1] - PMV[0][back][1], vert_f_code);
    		slice_putbits(engine, mb->mv_field_sel[1][back], 1);
    		putmv(engine, mb->MV[1][back][0] - PMV[1][back][0], hor_f_code);
    		putmv(engine, mb->MV[1][back][1] - PMV[1][back][1], vert_f_code);
    		PMV[0][back][0] = mb->MV[0][back][0];
    		PMV[0][back][1] = mb->MV[0][back][1];
    		PMV[1][back][0] = mb->MV[1][back][0];
    		PMV[1][back][1] = mb->MV[1][back][1];
    	}
    	else
    	{
    		/* dual prime prediction */
    		putmv(engine, mb->MV[0][back][0] - PMV[0][back][0], hor_f_code);
    		putdmv(engine, mb->dmvector[0]);
    		putmv(engine, mb->MV[0][back][1] - PMV[0][back][1], vert_f_code);
    		putdmv(engine, mb->dmvector[1]);
    		PMV[0][back][0] = PMV[1][back][0] = mb->MV[0][back][0];
    		PMV[0][back][1] = PMV[1][back][1] = mb->MV[0][back][1];
    	}
	}
}


void* slice_engine_loop(slice_engine_t *engine)
{
	while(!engine->done)
	{
		pthread_mutex_lock(&(engine->input_lock));

		if(!engine->done)
		{
			pict_data_s *picture = engine->picture;
			int i, j, k, comp, cc;
			int mb_type;
			int PMV[2][2][2];
			int cbp, MBAinc = 0;
			short (*quant_blocks)[64] = picture->qblocks;

			k = engine->start_row * mb_width;
			for(j = engine->start_row; j < engine->end_row; j++)
			{
/* macroblock row loop */
//printf("putpic 1\n");
//slice_testbits(engine);
    			for(i = 0; i < mb_width; i++)
    			{
					mbinfo_s *cur_mb = &picture->mbinfo[k];
					int cur_mb_blocks = k * block_count;
//pthread_mutex_lock(&test_lock);
/* macroblock loop */
    				if(i == 0)
    				{
						slice_alignbits(engine);
/* slice header (6.2.4) */
        				if(mpeg1 || vertical_size <= 2800)
        					slice_putbits(engine, SLICE_MIN_START + j, 32); /* slice_start_code */
        				else
        				{
        					slice_putbits(engine, SLICE_MIN_START + (j & 127), 32); /* slice_start_code */
        					slice_putbits(engine, j >> 7, 3); /* slice_vertical_position_extension */
        				}
/* quantiser_scale_code */
//printf("putpic 1\n");slice_testbits(engine);
        				slice_putbits(engine, 
							picture->q_scale_type ? map_non_linear_mquant_hv[engine->prev_mquant] : engine->prev_mquant >> 1, 
							5);

        				slice_putbits(engine, 0, 1); /* extra_bit_slice */

//printf("putpic 1 %d %d %d\n",engine->prev_mquant, map_non_linear_mquant_hv[engine->prev_mquant], engine->prev_mquant >> 1);
//slice_testbits(engine);
        				/* reset predictors */

        				for(cc = 0; cc < 3; cc++)
        					engine->dc_dct_pred[cc] = 0;

        				PMV[0][0][0] = PMV[0][0][1] = PMV[1][0][0] = PMV[1][0][1] = 0;
        				PMV[0][1][0] = PMV[0][1][1] = PMV[1][1][0] = PMV[1][1][1] = 0;

        				MBAinc = i + 1; /* first MBAinc denotes absolute position */
    				}

    				mb_type = cur_mb->mb_type;

/* determine mquant (rate control) */
    				cur_mb->mquant = ratectl_calc_mquant(engine->ratectl, picture, k);

/* quantize macroblock */
//printf("putpic 1\n");
//printf("putpic 1\n");
//slice_testbits(engine);
    				if(mb_type & MB_INTRA)
    				{
//printf("putpic 2 %d\n", cur_mb->mquant);
						quant_intra_hv( picture,
					        		 picture->blocks[cur_mb_blocks],
									 quant_blocks[cur_mb_blocks],
									 cur_mb->mquant, 
									 &cur_mb->mquant );

//printf("putpic 3\n");
						cur_mb->cbp = cbp = (1<<block_count) - 1;
    				}
    				else
    				{
//printf("putpic 4 %p %d\n", picture->blocks[cur_mb_blocks], cur_mb_blocks);
						cbp = (*pquant_non_intra)(picture,
											  picture->blocks[cur_mb_blocks],
											  quant_blocks[cur_mb_blocks],
											  cur_mb->mquant,
											  &cur_mb->mquant);
//printf("putpic 5\n");
						cur_mb->cbp = cbp;
						if (cbp)
							mb_type|= MB_PATTERN;
    				}
//printf("putpic 6\n");
//printf("putpic 2\n");
//slice_testbits(engine);

/* output mquant if it has changed */
    				if(cbp && engine->prev_mquant != cur_mb->mquant)
        				mb_type |= MB_QUANT;

/* check if macroblock can be skipped */
    				if(i != 0 && i != mb_width - 1 && !cbp)
    				{
/* no DCT coefficients and neither first nor last macroblock of slice */

        				if(picture->pict_type == P_TYPE && 
							!(mb_type & MB_FORWARD))
        				{
        					/* P picture, no motion vectors -> skip */

        					/* reset predictors */

        					for(cc = 0; cc < 3; cc++)
            					 engine->dc_dct_pred[cc] = 0;

        					PMV[0][0][0] = PMV[0][0][1] = PMV[1][0][0] = PMV[1][0][1] = 0;
        					PMV[0][1][0] = PMV[0][1][1] = PMV[1][1][0] = PMV[1][1][1] = 0;

        					cur_mb->mb_type = mb_type;
        					cur_mb->skipped = 1;
        					MBAinc++;
        					k++;
        					continue;
        				}

        				if(picture->pict_type == B_TYPE && 
							picture->pict_struct == FRAME_PICTURE &&
            				cur_mb->motion_type == MC_FRAME && 
            				((picture->mbinfo[k - 1].mb_type ^ mb_type) & (MB_FORWARD | MB_BACKWARD)) == 0 &&
            				(!(mb_type & MB_FORWARD) ||
                				(PMV[0][0][0] == cur_mb->MV[0][0][0] &&
                				 PMV[0][0][1] == cur_mb->MV[0][0][1])) && 
            				(!(mb_type & MB_BACKWARD) ||
                				(PMV[0][1][0] == cur_mb->MV[0][1][0] &&
                				 PMV[0][1][1] == cur_mb->MV[0][1][1])))
        				{
/* conditions for skipping in B frame pictures:
 * - must be frame predicted
 * - must be the same prediction type (forward/backward/interp.)
 *   as previous macroblock
 * - relevant vectors (forward/backward/both) have to be the same
 *   as in previous macroblock
 */

        					cur_mb->mb_type = mb_type;
        					cur_mb->skipped = 1;
        					MBAinc++;
        					k++;
        					continue;
        				}

        				if (picture->pict_type == B_TYPE && 
							picture->pict_struct != FRAME_PICTURE && 
            				cur_mb->motion_type == MC_FIELD && 
            				((picture->mbinfo[k - 1].mb_type ^ mb_type) & (MB_FORWARD | MB_BACKWARD))==0 && 
            				(!(mb_type & MB_FORWARD) ||
                				(PMV[0][0][0] == cur_mb->MV[0][0][0] &&
                				 PMV[0][0][1] == cur_mb->MV[0][0][1] &&
                				 cur_mb->mv_field_sel[0][0] == (picture->pict_struct == BOTTOM_FIELD))) && 
            				(!(mb_type & MB_BACKWARD) ||
                				(PMV[0][1][0] == cur_mb->MV[0][1][0] &&
                				 PMV[0][1][1] == cur_mb->MV[0][1][1] &&
                				 cur_mb->mv_field_sel[0][1] == (picture->pict_struct == BOTTOM_FIELD))))
        				{
/* conditions for skipping in B field pictures:
 * - must be field predicted
 * - must be the same prediction type (forward/backward/interp.)
 *   as previous macroblock
 * - relevant vectors (forward/backward/both) have to be the same
 *   as in previous macroblock
 * - relevant motion_vertical_field_selects have to be of same
 *   parity as current field
 */

        					cur_mb->mb_type = mb_type;
        					cur_mb->skipped = 1;
        					MBAinc++;
        					k++;
        					continue;
        				}
    				}
//printf("putpic 3\n");
//slice_testbits(engine);

/* macroblock cannot be skipped */
    				cur_mb->skipped = 0;

/* there's no VLC for 'No MC, Not Coded':
 * we have to transmit (0,0) motion vectors
 */
    				if(picture->pict_type == P_TYPE && 
						!cbp && 
						!(mb_type & MB_FORWARD))
        				mb_type |= MB_FORWARD;

/* macroblock_address_increment */
    				putaddrinc(engine, MBAinc); 
    				MBAinc = 1;
    				putmbtype(engine, picture->pict_type, mb_type); /* macroblock type */
//printf("putpic 3\n");
//slice_testbits(engine);

    				if(mb_type & (MB_FORWARD | MB_BACKWARD) && 
						!picture->frame_pred_dct)
        				slice_putbits(engine, cur_mb->motion_type, 2);

//printf("putpic %x %d %d %d\n", mb_type, picture->pict_struct, cbp, picture->frame_pred_dct);
    				if(picture->pict_struct == FRAME_PICTURE && 
						cbp && 
						!picture->frame_pred_dct)
        				slice_putbits(engine, cur_mb->dct_type, 1);

    				if(mb_type & MB_QUANT)
    				{
        				slice_putbits(engine, 
							picture->q_scale_type ? map_non_linear_mquant_hv[cur_mb->mquant] : cur_mb->mquant >> 1,
							5);
        				engine->prev_mquant = cur_mb->mquant;
    				}

    				if(mb_type & MB_FORWARD)
    				{
/* forward motion vectors, update predictors */
        				putmvs(engine, picture, cur_mb, PMV, 0);
    				}

    				if(mb_type & MB_BACKWARD)
    				{
/* backward motion vectors, update predictors */
        				putmvs(engine, picture,  cur_mb, PMV, 1);
    				}

    				if(mb_type & MB_PATTERN)
    				{
        				putcbp(engine, (cbp >> (block_count - 6)) & 63);
        				if(chroma_format != CHROMA420)
        					slice_putbits(engine, cbp, block_count - 6);
    				}

//printf("putpic 4\n");
//slice_testbits(engine);

    				for(comp = 0; comp < block_count; comp++)
    				{
/* block loop */
        				if(cbp & (1 << (block_count - 1 - comp)))
        				{
        				  if(mb_type & MB_INTRA)
        				  {
            				  cc = (comp < 4) ? 0 : (comp & 1) + 1;
            				  putintrablk(engine, picture, quant_blocks[cur_mb_blocks + comp], cc);

        				  }
        				  else
            				  putnonintrablk(engine, picture, quant_blocks[cur_mb_blocks + comp]);
        				}
    				}
//printf("putpic 5\n");
//slice_testbits(engine);
//sleep(1);

    				/* reset predictors */
    				if(!(mb_type & MB_INTRA))
        				for(cc = 0; cc < 3; cc++)
        					engine->dc_dct_pred[cc] = 0;

    				if(mb_type & MB_INTRA || 
						(picture->pict_type == P_TYPE && !(mb_type & MB_FORWARD)))
    				{
        				PMV[0][0][0] = PMV[0][0][1] = PMV[1][0][0] = PMV[1][0][1] = 0;
        				PMV[0][1][0] = PMV[0][1][1] = PMV[1][1][0] = PMV[1][1][1] = 0;
    				}

    				cur_mb->mb_type = mb_type;
    				k++;
//pthread_mutex_unlock(&test_lock);
    			}
			}
		}
		pthread_mutex_unlock(&(engine->output_lock));
	}
	return 0;
}


/* quantization / variable length encoding of a complete picture */
void putpict(pict_data_s *picture)
{
	int i;

	for(i = 0; i < processors; i++)
	{
		ratectl_init_pict(ratectl[i], picture); /* set up rate control */
	}

/* picture header and picture coding extension */
	putpicthdr(picture);
	if(!mpeg1) putpictcodext(picture);
/* Flush buffer before switching to slice mode */
	alignbits();

/* Start loop */
	for(i = 0; i < processors; i++)
	{
		slice_engines[i].prev_mquant = ratectl_start_mb(ratectl[i], picture);
		slice_engines[i].picture = picture;
		slice_engines[i].ratectl = ratectl[i];
		slice_initbits(&slice_engines[i]);

		pthread_mutex_unlock(&(slice_engines[i].input_lock));
	}

/* Wait for completion and write slices */
	for(i = 0; i < processors; i++)
	{
		pthread_mutex_lock(&(slice_engines[i].output_lock));
		slice_finishslice(&slice_engines[i]);
	}

	for(i = 0; i < processors; i++)
		ratectl_update_pict(ratectl[i], picture);
}

void start_slice_engines()
{
	int i;
	int rows_per_processor = (int)((float)mb_height2 / processors + 0.5);
	int current_row = 0;
	pthread_attr_t attr;
	pthread_mutexattr_t mutex_attr;

	pthread_mutexattr_init(&mutex_attr);
	pthread_attr_init(&attr);
//	pthread_mutex_init(&test_lock, &mutex_attr);

	slice_engines = calloc(1, sizeof(slice_engine_t) * processors);
	for(i = 0; i < processors; i++)
	{
		slice_engines[i].start_row = current_row;
		current_row += rows_per_processor;
		if(current_row > mb_height2) current_row = mb_height2;
		slice_engines[i].end_row = current_row;
		
		pthread_mutex_init(&(slice_engines[i].input_lock), &mutex_attr);
		pthread_mutex_lock(&(slice_engines[i].input_lock));
		pthread_mutex_init(&(slice_engines[i].output_lock), &mutex_attr);
		pthread_mutex_lock(&(slice_engines[i].output_lock));
		slice_engines[i].done = 0;
		pthread_create(&(slice_engines[i].tid), 
			&attr, 
			(void*)slice_engine_loop, 
			&slice_engines[i]);
	}
}

void stop_slice_engines()
{
	int i;
	for(i = 0; i < processors; i++)
	{
		slice_engines[i].done = 1;
		pthread_mutex_unlock(&(slice_engines[i].input_lock));
		pthread_join(slice_engines[i].tid, 0);
		pthread_mutex_destroy(&(slice_engines[i].input_lock));
		pthread_mutex_destroy(&(slice_engines[i].output_lock));
		if(slice_engines[i].slice_buffer) free(slice_engines[i].slice_buffer);
	}
	free(slice_engines);
//	pthread_mutex_destroy(&test_lock);
}
