/*
* Java port of parts of the ffmpeg Mpeg4 base decoder.
* Copyright (c) 2003 Jonathan Hueber.
*
* Copyright (c) 2001 Fabrice Bellard.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* See Credits file and Readme for details
*/
package net.sourceforge.jffmpeg.codecs.video.mpeg4.div3;
import net.sourceforge.jffmpeg.codecs.video.mpeg.DisplayOutput;
import net.sourceforge.jffmpeg.codecs.video.mpeg4.Mpeg4Exception;
import net.sourceforge.jffmpeg.codecs.utils.FFMpegException;
import net.sourceforge.jffmpeg.codecs.utils.BitStream;
/**
* MPEG4 base decoder. This contains many functions common to various
* Mpeg4-like video codecs.
*/
public abstract class Mpeg4 {
/**
* PictType values
*/
public static final int I_FRAME_TYPE = 1;
public static final int P_FRAME_TYPE = 2;
public static final int B_FRAME_TYPE = 3;
/**
* Input handler
*/
protected BitStream in = new BitStream();
/**
* Decoder state
*/
protected boolean truncatedFlag;
protected int pictType;
protected int qscale;
protected int sliceHeight;
protected int bitRate;
protected boolean flipFlopRounding;
protected boolean perMbRlTable;
protected int rlChromaTableIndex;
protected int rlTableIndex;
protected int dcTableIndex;
protected boolean interIntraPred;
protected boolean ac_Pred;
protected int h263_aic_dir;
protected boolean inter_intra_pred;
/**
* Predefined values
*/
protected int mbWidth;
protected int mbHeight;
protected int[] blankBlock = new int[ 64 ];
public static final int NUMBER_OF_BLOCKS = 6;
protected int[] blockIndex = new int[ NUMBER_OF_BLOCKS ];
protected int[] blockWrap = new int[ NUMBER_OF_BLOCKS ];
protected int[][] block = new int[ NUMBER_OF_BLOCKS ][ 64 ];
protected boolean []skipBlock = new boolean[ NUMBER_OF_BLOCKS ];
protected int[] yDcScaleTable;
protected int[] cDcScaleTable;
protected int y_dc_scale = 8; // from q_scale
protected int c_dc_scale = 8;
protected int[] dc_val; //DC values
protected int[] ac_val; //AC values
protected int[][] motion_val; //Motion values
protected int pel_motionX;
protected int pel_motionY;
protected int resync_mb_x;
protected boolean use_skip_mb_code;
/**
* If we skip a macroblock twice in a row, we do nothing
*/
protected boolean[][] skipTable;
protected boolean mb_skipped;
protected int mv_table_index;
protected boolean mb_intra;
protected int[] coded_block;
protected boolean h263_pred = true;
protected boolean h263_aic;
protected int[] mbintra_table;
protected boolean dc_pred_dir;
protected boolean ac_pred;
protected boolean no_rounding = true;
/**
* Output
*/
protected DisplayOutput displayOutput;
/**
* Decode a frame
*/
protected abstract void decodeFrame( byte[] frameData, int dataLength ) throws FFMpegException;
/**
* Unquantize DCT values for B frames
*/
protected void dct_unquantize_h263( int blockNumber ) {
int i = 0;
if ( mb_intra ) {
block[ blockNumber ][ 0 ] *= (blockNumber < 4 ) ? y_dc_scale : c_dc_scale;
i = 1;
}
int qmul = qscale << 1;
int qadd = (qscale - 1) | 1;
for ( ; i < 64; i++ ) {
if ( block[ blockNumber ][ i ] > 0 ) {
block[ blockNumber ][ i ] = block[ blockNumber ][ i ] * qmul + qadd;
} else if ( block[ blockNumber ][ i ] < 0 ) {
block[ blockNumber ][ i ] = block[ blockNumber ][ i ] * qmul - qadd;
}
}
}
/*
* Display a Macroblock
* - DCT for I macroblocks
* - motion followed by addition of DCT for P macroblocks
*/
protected void MPV_decode_mb( int x, int y ) {
if ( !mb_intra ) {
/*
* Predicted macroblock
*/
if ( h263_pred || h263_aic ) {
if ( mbintra_table[ y * mbWidth + x ] != 0 ) {
mbintra_table[ y * mbWidth + x ] = 0;
/** ff_clean_intra_table_entries */
int xy = blockIndex[0];
int wrap = blockWrap[0];
dc_val[ xy ] = 0x400;
dc_val[ xy + 1 ] = 0x400;
dc_val[ xy + wrap ] = 0x400;
dc_val[ xy + 1 + wrap ] = 0x400;
/** ac_val */
System.arraycopy( blankBlock, 0, ac_val, xy * 16, 32 );
System.arraycopy( blankBlock, 0, ac_val, (xy + wrap)*16, 32 );
/** Chroma */
wrap = blockWrap[4];
xy = blockWrap[ 0 ] * ( mbHeight * 2 + 2 )
+ x + 1 + (y + 1) * wrap;
dc_val[xy] = 0x400;
dc_val[xy + wrap * (mbHeight+2) ] = 0x400;
/* ac pred */
System.arraycopy( blankBlock, 0, ac_val, xy*16, 16 );
System.arraycopy( blankBlock, 0, ac_val, (xy + wrap * (mbHeight + 2))*16, 16 );
}
}
/* Can we skip this mb? - note frame buffer */
if ( !(mb_skipped && skipTable[ x ][ y ]) ) {
skipTable[ x ][ y ] = mb_skipped;
/* Motion code */
displayOutput.move( x, y, pel_motionX, pel_motionY, !no_rounding );
displayOutput.addLuminanceIdct( x * 2, y * 2, block[ 0 ] );
displayOutput.addLuminanceIdct( x * 2 + 1, y * 2, block[ 1 ] );
displayOutput.addLuminanceIdct( x * 2, y * 2 + 1, block[ 2 ] );
displayOutput.addLuminanceIdct( x * 2 + 1, y * 2 + 1, block[ 3 ] );
displayOutput.addRedIdct( x, y, block[ 5 ] );
displayOutput.addBlueIdct( x, y, block[ 4 ] );
}
} else {
/**
* I type macroblock
*/
skipTable[ x ][ y ] = false;
if ( h263_pred || h263_aic ) {
mbintra_table[ y * mbWidth + x ] = 1;
}
/**
* Not motion
*/
pel_motionX = 0;
pel_motionY = 0;
/* Display Macro block */
int mbX = x;
int mbY = y;
dct_unquantize_h263( 0 );
dct_unquantize_h263( 1 );
dct_unquantize_h263( 2 );
dct_unquantize_h263( 3 );
dct_unquantize_h263( 4 );
dct_unquantize_h263( 5 );
displayOutput.putLuminanceIdct( x * 2, y * 2, block[ 0 ] );
displayOutput.putLuminanceIdct( x * 2 + 1, y * 2, block[ 1 ] );
displayOutput.putLuminanceIdct( x * 2, y * 2 + 1, block[ 2 ] );
displayOutput.putLuminanceIdct( x * 2 + 1, y * 2 + 1, block[ 3 ] );
displayOutput.putRedIdct( x, y, block[ 5 ] );
displayOutput.putBlueIdct( x, y, block[ 4 ] );
}
/**
* Motion cache
*/
for ( int i = 0; i < 4; i++ ) {
motion_val[blockIndex[ i ]][0] = pel_motionX;
motion_val[blockIndex[ i ]][1] = pel_motionY;
}
/**
* Clear blocks for next round
*/
System.arraycopy( blankBlock, 0, block[0], 0, 64 );
System.arraycopy( blankBlock, 0, block[1], 0, 64 );
System.arraycopy( blankBlock, 0, block[2], 0, 64 );
System.arraycopy( blankBlock, 0, block[3], 0, 64 );
System.arraycopy( blankBlock, 0, block[4], 0, 64 );
System.arraycopy( blankBlock, 0, block[5], 0, 64 );
}
/**
* Constructor does nothing
*/
protected Mpeg4() {}
/**
* Initialise the width and height of this codec
*/
protected void initialise( int width, int height ) {
/**
* Calculate number of macroblocks
*/
mbWidth = (width + 15) /16;
mbHeight = (height + 15) / 16;
/**
* Block wrapping
*/
blockWrap[ 0 ] = mbWidth * 2 + 2;
blockWrap[ 1 ] = mbWidth * 2 + 2;
blockWrap[ 2 ] = mbWidth * 2 + 2;
blockWrap[ 3 ] = mbWidth * 2 + 2;
blockWrap[ 4 ] = mbWidth + 2;
blockWrap[ 5 ] = mbWidth + 2;
/**
* Initialise DC / AC / motion prediction tables
*/
dc_val = new int[ 1 + (mbWidth * 2 + 2) * mbHeight * 2 * 4 ];
ac_val = new int[ 1 + (mbWidth * 2 + 2) * mbHeight * 16 * 4 * 16];
motion_val = new int[ 1 + (mbWidth * 2 + 2) * mbHeight * 2 * 4 ][2];
skipTable = new boolean[ mbWidth ][ mbHeight ];
for ( int i = 0; i < dc_val.length; i++ ) {
dc_val[i] = 0x400;
ac_val[i] = 0;
}
coded_block = new int[ 1 + (mbWidth * 2 + 2) * mbHeight * 16 * 4 ];
mbintra_table = new int[ 1 + (mbWidth * 2 + 2) * mbHeight * 2 * 4 ];
for ( int y = 0; y < mbintra_table.length; y++ ) mbintra_table[ y ] = 1;
/*
* Initialise output
*/
displayOutput = new DisplayOutput( mbWidth, mbHeight );
}
}