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

 
/*  (C) 2000-2005 Andrew Stevens */

/* These modifications are free software; you can redistribute it
 *  and/or modify it under the terms of the GNU General Public License
 *  as published by the Free Software Foundation; either version 2 of
 *  the License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 *
 */
 
/* Original reference encoder from which this derived:
 * 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 <config.h>
#include <stdio.h>
#include <cassert>
#include "mpeg2syntaxcodes.h"
#include "tables.h"
#include "simd.h"
#include "mpeg2encoder.hh"
#include "mpeg2coder.hh"
#include "ratectl.hh"
#include "macroblock.hh"
#include "picture.hh"

    
/* output motion vectors (6.2.5.2, 6.3.16.2)
 *
 * this routine also updates the predictions for motion vectors (PMV)
 */
void Picture::PutMVs( MotionEst &me, bool back )

{
	int hor_f_code;
	int vert_f_code;

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

	if (pict_struct==FRAME_PICTURE)
	{
		if (me.motion_type==MC_FRAME)
		{
			/* frame prediction */
			coding->PutMV(me.MV[0][back][0]-PMV[0][back][0],hor_f_code);
			coding->PutMV(me.MV[0][back][1]-PMV[0][back][1],vert_f_code);
			PMV[0][back][0]=PMV[1][back][0]=me.MV[0][back][0];
			PMV[0][back][1]=PMV[1][back][1]=me.MV[0][back][1];
		}
		else if (me.motion_type==MC_FIELD)
		{
			/* field prediction */

			coding->PutBits(me.field_sel[0][back],1);
			coding->PutMV(me.MV[0][back][0]-PMV[0][back][0],hor_f_code);
			coding->PutMV((me.MV[0][back][1]>>1)-(PMV[0][back][1]>>1),vert_f_code);
			coding->PutBits(me.field_sel[1][back],1);
			coding->PutMV(me.MV[1][back][0]-PMV[1][back][0],hor_f_code);
			coding->PutMV((me.MV[1][back][1]>>1)-(PMV[1][back][1]>>1),vert_f_code);
			PMV[0][back][0]=me.MV[0][back][0];
			PMV[0][back][1]=me.MV[0][back][1];
			PMV[1][back][0]=me.MV[1][back][0];
			PMV[1][back][1]=me.MV[1][back][1];

		}
		else
		{
#ifdef DEBUG_DPME
            MotionVector DMV[Parity::dim /*pred*/];
                        calc_DMV(*this,
                         DMV,
                         me.dualprimeMV,
                         me.MV[0][0][0],
                         me.MV[0][0][1]>>1);
                         
            printf( "PR%06d: %03d %03d %03d %03d %03d %03d\n", dp_mv,
                me.MV[0][0][0], (me.MV[0][0][1]>>1), DMV[0][0], DMV[0][1], DMV[1][0], DMV[1][1] );
            ++dp_mv;
            if( dp_mv == 45000 )
                exit(0);
#endif
			/* dual prime prediction */
			coding->PutMV(me.MV[0][back][0]-PMV[0][back][0],hor_f_code);
			coding->PutDMV(me.dualprimeMV[0]);
			coding->PutMV((me.MV[0][back][1]>>1)-(PMV[0][back][1]>>1),vert_f_code);
			coding->PutDMV(me.dualprimeMV[1]);
			PMV[0][back][0]=PMV[1][back][0]=me.MV[0][back][0];
			PMV[0][back][1]=PMV[1][back][1]=me.MV[0][back][1];
		}
	}
	else
	{
		/* field picture */
		if (me.motion_type==MC_FIELD)
		{
			/* field prediction */
			coding->PutBits(me.field_sel[0][back],1);
			coding->PutMV(me.MV[0][back][0]-PMV[0][back][0],hor_f_code);
			coding->PutMV(me.MV[0][back][1]-PMV[0][back][1],vert_f_code);
			PMV[0][back][0]=PMV[1][back][0]=me.MV[0][back][0];
			PMV[0][back][1]=PMV[1][back][1]=me.MV[0][back][1];
		}
		else if (me.motion_type==MC_16X8)
		{
			/* 16x8 prediction */
			coding->PutBits(me.field_sel[0][back],1);
			coding->PutMV(me.MV[0][back][0]-PMV[0][back][0],hor_f_code);
			coding->PutMV(me.MV[0][back][1]-PMV[0][back][1],vert_f_code);
			coding->PutBits(me.field_sel[1][back],1);
			coding->PutMV(me.MV[1][back][0]-PMV[1][back][0],hor_f_code);
			coding->PutMV(me.MV[1][back][1]-PMV[1][back][1],vert_f_code);
			PMV[0][back][0]=me.MV[0][back][0];
			PMV[0][back][1]=me.MV[0][back][1];
			PMV[1][back][0]=me.MV[1][back][0];
			PMV[1][back][1]=me.MV[1][back][1];
		}
		else
		{
			/* dual prime prediction */
			coding->PutMV(me.MV[0][back][0]-PMV[0][back][0],hor_f_code);
			coding->PutDMV(me.dualprimeMV[0]);
			coding->PutMV(me.MV[0][back][1]-PMV[0][back][1],vert_f_code);
			coding->PutDMV(me.dualprimeMV[1]);
			PMV[0][back][0]=PMV[1][back][0]=me.MV[0][back][0];
			PMV[0][back][1]=PMV[1][back][1]=me.MV[0][back][1];
		}
	}
}

void MacroBlock::PutBlocks( )
{
    int comp;
    int cc;
    for (comp=0; comp<BLOCK_COUNT; comp++)
    {
        /* block loop */
        if( cbp & (1<<(BLOCK_COUNT-1-comp)))
        {
            if (final_me.mb_type & MB_INTRA)
            {
                // TODO: 420 Only?
                cc = (comp<4) ? 0 : (comp&1)+1;
                picture->coding->PutIntraBlk(picture, qdctblocks[comp],cc);
            }
            else
            {
                picture->coding->PutNonIntraBlk(picture,qdctblocks[comp]);
            }
        }
    }
}

void MacroBlock::SkippedCoding( bool slice_begin_end )
{
    skipped = false;
    if( slice_begin_end || cbp )
    {
        /* there's no VLC for 'No MC, Not Coded':
         * we have to transmit (0,0) motion vectors
         */
        if (picture->pict_type==P_TYPE && !cbp)
            final_me.mb_type|= MB_FORWARD;
        return;
    }

    MacroBlock *prev_mb = picture->prev_mb;
    /* P picture, no motion vectors -> skip */
    if (picture->pict_type==P_TYPE && !(final_me.mb_type&MB_FORWARD))
    {
        /* reset predictors */
        picture->Reset_DC_DCT_Pred();
        picture->Reset_MV_Pred();
        skipped = true;
        return;
    }
    
    if(picture->pict_type==B_TYPE )
    {
        /* B frame picture with same prediction type
         * (forward/backward/interp.)  and same active vectors
         * as in previous macroblock -> skip
         */

        if (  picture->pict_struct==FRAME_PICTURE
              && final_me.motion_type==MC_FRAME
              && ((prev_mb->final_me.mb_type ^ final_me.mb_type) &(MB_FORWARD|MB_BACKWARD))==0
              && (!(final_me.mb_type&MB_FORWARD) ||
                  (picture->PMV[0][0][0]==final_me.MV[0][0][0] &&
                   picture->PMV[0][0][1]==final_me.MV[0][0][1]))
              && (!(final_me.mb_type&MB_BACKWARD) ||
                  (picture->PMV[0][1][0]==final_me.MV[0][1][0] &&
                   picture->PMV[0][1][1]==final_me.MV[0][1][1])))
        {
            skipped = true;
            return;
        }

        /* B field picture macroblock with same prediction
         * type (forward/backward/interp.) and active
         * vectors as previous macroblock and same
         * vertical field selects as current field -> skio
         */

        if (picture->pict_struct!=FRAME_PICTURE
            && final_me.motion_type==MC_FIELD
            && ((prev_mb->final_me.mb_type^final_me.mb_type)&(MB_FORWARD|MB_BACKWARD))==0
            && (!(final_me.mb_type&MB_FORWARD) ||
                (picture->PMV[0][0][0]==final_me.MV[0][0][0] &&
                 picture->PMV[0][0][1]==final_me.MV[0][0][1] &&
                 final_me.field_sel[0][0]==(picture->pict_struct==BOTTOM_FIELD)))
            && (!(final_me.mb_type&MB_BACKWARD) ||
                (picture->PMV[0][1][0]==final_me.MV[0][1][0] &&
                 picture->PMV[0][1][1]==final_me.MV[0][1][1] &&
                 final_me.field_sel[0][1]==(picture->pict_struct==BOTTOM_FIELD))))
        {
            skipped = true;
            return;
        }
    }

}


/* generate picture header (6.2.3, 6.3.10) */
void Picture::PutHeader()
{
	assert( coding->Aligned() );
	coding->PutBits(PICTURE_START_CODE,32); /* picture_start_code */
	coding->PutBits(temp_ref,10); /* temporal_reference */
	coding->PutBits(pict_type,3); /* picture_coding_type */
	coding->PutBits(vbv_delay,16); /* vbv_delay */

	if (pict_type==P_TYPE || pict_type==B_TYPE)
	{
		coding->PutBits(0,1); /* full_pel_forward_vector */
		if (encparams.mpeg1)
			coding->PutBits(forw_hor_f_code,3);
		else
			coding->PutBits(7,3); /* forward_f_code */
	}

	if (pict_type==B_TYPE)
	{
		coding->PutBits(0,1); /* full_pel_backward_vector */
		if (encparams.mpeg1)
			coding->PutBits(back_hor_f_code,3);
		else
			coding->PutBits(7,3); /* backward_f_code */
	}
	coding->PutBits(0,1); /* extra_bit_picture */
    coding->AlignBits();
	if ( !encparams.mpeg1 )
	{
		PutCodingExt();
	}

}

/* generate picture coding extension (6.2.3.1, 6.3.11)
 *
 * composite display information (v_axis etc.) not implemented
 */
void Picture::PutCodingExt()
{
	assert( coding->Aligned() );
	coding->PutBits(EXT_START_CODE,32); /* extension_start_code */
	coding->PutBits(CODING_ID,4); /* extension_start_code_identifier */
	coding->PutBits(forw_hor_f_code,4); /* forward_horizontal_f_code */
	coding->PutBits(forw_vert_f_code,4); /* forward_vertical_f_code */
	coding->PutBits(back_hor_f_code,4); /* backward_horizontal_f_code */
	coding->PutBits(back_vert_f_code,4); /* backward_vertical_f_code */
	coding->PutBits(dc_prec,2); /* intra_dc_precision */
	coding->PutBits(pict_struct,2); /* picture_structure */
	coding->PutBits((pict_struct==FRAME_PICTURE)?topfirst : 0, 1); /* top_field_first */
	coding->PutBits(frame_pred_dct,1); /* frame_pred_frame_dct */
	coding->PutBits(0,1); /* concealment_motion_vectors  -- currently not implemented */
	coding->PutBits(q_scale_type,1); /* q_scale_type */
	coding->PutBits(intravlc,1); /* intra_vlc_format */
	coding->PutBits(altscan,1); /* alternate_scan */
	coding->PutBits(repeatfirst,1); /* repeat_first_field */

	coding->PutBits(prog_frame,1); /* chroma_420_type */
	coding->PutBits(prog_frame,1); /* progressive_frame */
	coding->PutBits(0,1); /* composite_display_flag */
    coding->AlignBits();
}


void Picture::PutSliceHdr( int slice_mb_y, int mquant )
{
    /* slice header (6.2.4) */
    coding->AlignBits();
    
    if (encparams.mpeg1 || encparams.vertical_size<=2800)
        coding->PutBits(SLICE_MIN_START+slice_mb_y,32); /* slice_start_code */
    else
    {
        coding->PutBits(SLICE_MIN_START+(slice_mb_y&127),32); /* slice_start_code */
        coding->PutBits(slice_mb_y>>7,3); /* slice_vertical_position_extension */
    }
    
    /* quantiser_scale_code */
    coding->PutBits(q_scale_type 
            ? map_non_linear_mquant[mquant] 
            : mquant >> 1, 5);
    
    coding->PutBits(0,1); /* extra_bit_slice */
    
} 



/* **********************************
 * 
 * PutHeaders - Put sequence of headers and user data elements that 'belong'
 * to this frame.  We count sequence and GOP headers as belong to the first
 * following picture.
 * 
 * ********************************/
 
void Picture::PutHeaders()
{
    /* Sequence header if new sequence or we're generating for a
       format like (S)VCD that mandates sequence headers every GOP to
       do fast forward, rewind etc.
    */
    if( new_seq || decode == 0 || (gop_start && encparams.seq_hdr_every_gop) )
    {
      coding->PutSeqHdr();
    }
   
    if( gop_start )
    {
      coding->PutGopHdr( decode,  closed_gop );
    }
    
    /* picture header and picture coding extension */
    PutHeader();

    /* TODO: This should really be a member of the picture object */
   if( encparams.svcd_scan_data && pict_type == I_TYPE )
   {
      coding->PutUserData( dummy_svcd_scan_data, sizeof(dummy_svcd_scan_data) );
   }
}


/* 
 * Local variables:
 *  c-file-style: "stroustrup"
 *  tab-width: 4
 *  indent-tabs-mode: nil
 * End:
 */
