/*
* Java port of ffmpeg DIVX decoder.
* Copyright (c) 2004 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
* 1a39e335700bec46ae31a38e2156a898
*/
package net.sourceforge.jffmpeg.codecs.video.mpeg4.divx;
import javax.media.Codec;
import javax.media.Format;
import javax.media.format.VideoFormat;
import javax.media.Buffer;
import javax.media.format.RGBFormat;
import javax.media.format.YUVFormat;
import java.awt.Dimension;
import net.sourceforge.jffmpeg.JMFCodec;
import net.sourceforge.jffmpeg.codecs.video.mpeg4.Mpeg4Exception;
import net.sourceforge.jffmpeg.codecs.utils.FFMpegException;
import net.sourceforge.jffmpeg.codecs.utils.BitStream;
import net.sourceforge.jffmpeg.codecs.utils.VLCTable;
import net.sourceforge.jffmpeg.codecs.video.mpeg.DisplayOutput;
import net.sourceforge.jffmpeg.codecs.video.mpeg4.divx.vlc.*;
import net.sourceforge.jffmpeg.codecs.video.mpeg4.divx.rltables.*;
import net.sourceforge.jffmpeg.codecs.video.mpeg4.divx.tables.ScanTable;
import net.sourceforge.jffmpeg.codecs.video.mpeg4.div3.rltables.RLTable;
/**
* This is a JMF Video Codec.
* This is a port from ffmpeg - This version targets H263
*/
public class DIVXCodec implements Codec, JMFCodec {
public static final boolean debug = false;
public static final boolean debug2 = false;
/**
* Width and height
*/
int width, height;
int mbWidth, mbHeight;
/**
* Current picture cache
*/
private int[] mb_type;
private int[] cbp_table;
private int[] qscale_table;
private int[][] motion_val;
private int[] ac_val;
private int[] dc_val;
private int[] pred_dir_table;
private int[] mbintra_table;
/**
* mb_type (used in BFrame)
*/
private int[] mb_type_b_frame;
private int[] mb_type_ip_frame;
private int[][] next_motion_val;
/**
* The negotiated InputFormat
*/
private VideoFormat inputFormat;
private DisplayOutput displayOutput;
/**
* Input handler
*/
protected BitStream in = new BitStream();
private boolean[][] skipTable;
private boolean[][] mb_skiptable;
/**
* 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 ) {
int pel_motionX = mv[0][0][0];
int pel_motionY = mv[0][0][1];
boolean h263_pred = true;
boolean h263_aic = false;
qscale_table[ x + y * mbWidth ] = qscale;
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( blank, 0, ac_val, xy * 16 + mbWidth * 16, 32 );
System.arraycopy( blank, 0, ac_val, (xy + wrap)*16 + mbWidth * 16, 32 );
/** Chroma */
dc_val[blockIndex[4]] = 0x400;
dc_val[blockIndex[5]] = 0x400;
/* ac pred */
if ( debug2 ) DisplayOutput.debug.println( "ac_pred_clean" );
System.arraycopy( blank, 0, ac_val, blockIndex[4]*16 + mbWidth * 16, 16 );
System.arraycopy( blank, 0, ac_val, blockIndex[5]*16 + mbWidth * 16, 16 );
}
}
/* Store skipped MBs (for use in BFrame) */
if ( pict_type == I_TYPE || pict_type == P_TYPE )
{
mb_skiptable[ x ][ y ] = mb_skipped;
}
/* Can we skip this mb? - note frame buffer */
mb_skipped = false;
if ( !(mb_skipped && skipTable[ x ][ y ]) ) {
skipTable[ x ][ y ] = mb_skipped;
/* Motion code */
if ( pict_type == B_TYPE ) {
if ( (mv_dir & MV_DIR_FORWARD) != 0 ) { //testing
if ( mv_type == MV_TYPE_16X16 ) {
displayOutput.move( x, y, pel_motionX, pel_motionY, !no_rounding );
} else if ( mv_type == MV_TYPE_FIELD ) {
displayOutput.moveField( x, y, mv[0][0][0], mv[0][0][1], !no_rounding, false);
displayOutput.moveField( x, y, mv[0][1][0], mv[0][1][1], !no_rounding, true );
} else if ( mv_type == MV_TYPE_8X8 ) {
displayOutput.move8x8( x, y, mv[0][0][0], mv[0][0][1], !no_rounding, 0, 0 );
displayOutput.move8x8( x, y, mv[0][1][0], mv[0][1][1], !no_rounding, 8, 0 );
displayOutput.move8x8( x, y, mv[0][2][0], mv[0][2][1], !no_rounding, 0, 8 );
displayOutput.move8x8( x, y, mv[0][3][0], mv[0][3][1], !no_rounding, 8, 8 );
}
}
/* Backward motion (B Frames) */
if ( (mv_dir & MV_DIR_BACKWARD) != 0 ) {
boolean merge = (mv_dir & MV_DIR_FORWARD) != 0;
if ( mv_type == MV_TYPE_16X16 ) {
displayOutput.moveFromNext( x, y, mv[1][0][0], mv[1][0][1], !no_rounding, merge );
} else if ( mv_type == MV_TYPE_FIELD ) {
displayOutput.moveFieldFromNext( x, y, mv[1][0][0], mv[1][0][1], !no_rounding, false, merge);
displayOutput.moveFieldFromNext( x, y, mv[1][1][0], mv[1][1][1], !no_rounding, true, merge );
} else if ( mv_type == MV_TYPE_8X8 ) {
displayOutput.move8x8FromNext( x, y, mv[1][0][0], mv[1][0][1], !no_rounding, 0, 0, merge );
displayOutput.move8x8FromNext( x, y, mv[1][1][0], mv[1][1][1], !no_rounding, 8, 0, merge );
displayOutput.move8x8FromNext( x, y, mv[1][2][0], mv[1][2][1], !no_rounding, 0, 8, merge );
displayOutput.move8x8FromNext( x, y, mv[1][3][0], mv[1][3][1], !no_rounding, 8, 8, merge );
}
}
} else {
/* PFrame */
if ( mv_type == MV_TYPE_16X16 ) {
displayOutput.moveFromIPFrame( x, y, pel_motionX, pel_motionY, !no_rounding );
} else if ( mv_type == MV_TYPE_FIELD ) {
displayOutput.moveFieldFromIPFrame( x, y, mv[0][0][0], mv[0][0][1], !no_rounding, false);
displayOutput.moveFieldFromIPFrame( x, y, mv[0][1][0], mv[0][1][1], !no_rounding, true );
} else if ( mv_type == MV_TYPE_8X8 ) {
displayOutput.move8x8FromIPFrame( x, y, mv[0][0][0], mv[0][0][1], !no_rounding, 0, 0 );
displayOutput.move8x8FromIPFrame( x, y, mv[0][1][0], mv[0][1][1], !no_rounding, 8, 0 );
displayOutput.move8x8FromIPFrame( x, y, mv[0][2][0], mv[0][2][1], !no_rounding, 0, 8 );
displayOutput.move8x8FromIPFrame( x, y, mv[0][3][0], mv[0][3][1], !no_rounding, 8, 8 );
}
}
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.addBlueIdct( x, y, block[ 4 ] );
displayOutput.addRedIdct( x, y, block[ 5 ] );
}
} else {
/**
* I type macroblock
*/
skipTable[ x ][ y ] = false;
if ( h263_pred || h263_aic ) {
mbintra_table[ y * mbWidth + x ] = 1;
}
/**
* Not motion
*/
mv_type = MV_TYPE_16X16;
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.putBlueIdct( x, y, block[ 4 ] );
displayOutput.putRedIdct( x, y, block[ 5 ] );
}
/**
* Motion cache
*/
if ( pict_type != B_TYPE && debug ) System.out.println( "mv_type " + mv_type );
if ( pict_type != B_TYPE && mv_type != MV_TYPE_8X8) {
if ( debug ) System.out.println( "motion_x " + pel_motionX + " motion_y " + pel_motionY + " xy " + (blockIndex[0]-82) );
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( blank, 0, block[0], 0, 64 );
System.arraycopy( blank, 0, block[1], 0, 64 );
System.arraycopy( blank, 0, block[2], 0, 64 );
System.arraycopy( blank, 0, block[3], 0, 64 );
System.arraycopy( blank, 0, block[4], 0, 64 );
System.arraycopy( blank, 0, block[5], 0, 64 );
}
/**
* Set the width and height in pixels for this video stream
*/
protected void initialise( int width, int height ) {
this.width = width;
this.height = height;
mbWidth= (width+15) / 16;
mbHeight = (height+15) / 16;
mb_stride = mbWidth;
blockWrap[ 0 ] = mbWidth * 2 + 1;
blockWrap[ 1 ] = mbWidth * 2 + 1;
blockWrap[ 2 ] = mbWidth * 2 + 1;
blockWrap[ 3 ] = mbWidth * 2 + 1;
blockWrap[ 4 ] = mbWidth + 1;
blockWrap[ 5 ] = mbWidth + 1;
mb_type_b_frame = new int[ mbWidth * mbHeight * 2 ];
mb_type_ip_frame = new int[ mbWidth * mbHeight * 2 ];
cbp_table = new int[ mbWidth * mbHeight * 2 ];
qscale_table = new int[ mbWidth * mbHeight * 2 ];
for ( int i = 0; i < qscale_table.length; i++ ) {
qscale_table[i] = 4; /* 3 or 4 TODO figure this out!!! */
}
motion_val = new int[ mbWidth * mbHeight * 4 * 2][ 2 ];
ac_val = new int[ mbWidth * (mbHeight+2) * 4 * 16 * 2 ];
dc_val = new int[ mbWidth * mbHeight * 4 * 2 ];
pred_dir_table = new int[ mbWidth * mbHeight * 2];
skipTable = new boolean[ mbWidth * 2 ][mbHeight * 2];
mb_skiptable = new boolean[ mbWidth * 2 ][mbHeight * 2];
mbintra_table = new int[ mbWidth * mbHeight * 2 ];
/* TODO move this to the correct place */
for ( int i = 0; i < dc_val.length; i++ ) dc_val[i] = 1024;
displayOutput = new DisplayOutput( mbWidth, mbHeight );
}
/**
* Construct the codec. Initialization of the codec
* should be performced via setInputFormat.
*/
public DIVXCodec() {
super();
}
/**
* Construct the codec with the width and height of the
* video in pixels. This is the least data required to
* use the codec.
*/
public DIVXCodec(int width, int height) {
super();
initialise( width, height );
}
/**
* Retrieve the supported input formats. Currently "DIVX"
*
* @return Format[] the supported input formats
*/
public Format[] getSupportedInputFormats() {
return new Format[] { new VideoFormat( "DIVX" ) };
}
/**
* Retrieve the supported output formats. Currently RGBVideo
*
* @return Format[] the supported output formats
*/
public Format[] getSupportedOutputFormats(Format format) {
return new Format[] { new RGBFormat() };
}
/**
* Negotiate the format for the input data.
*
* Only the width and height entries are used
*
* @return Format the negotiated input format
*/
public Format setInputFormat( Format format ) {
inputFormat = (VideoFormat)format;
initialise( (int)inputFormat.getSize().getWidth(),
(int)inputFormat.getSize().getHeight() );
return format;
}
/**
* Negotiate the format for screen display renderer.
*
* Only the frame rate entry is used. All the other
* values are populated using the negotiated input formnat.
*
* @return Format RGBFormat to supply to display renderer.
*/
public Format setOutputFormat( Format format ) {
return new RGBFormat( new Dimension( mbWidth * 16, mbHeight * 16 ),
-1, (new int[0]).getClass(), // array
inputFormat.getFrameRate(), // Frames/sec
32, 0xff0000, 0x00ff00, 0x0000ff) ; //Colours
}
private static final int mid_pred( int a, int b, int c ) {
if ( a > b ) {
if ( c > b ) {
if ( c > a ) b=a;
else b=c;
}
} else {
if ( b > c ) {
if ( c > a ) b=c;
else b=a;
}
}
return b;
}
private int h263_predMotionX;
private int h263_predMotionY;
private void h263_pred_motion( int blockNumber ) {
boolean h263_pred = true;
int[] off = new int[] {2, 1, 1, -1};
int mot_val_offset = blockIndex[ blockNumber ];
int[] a;
int[] b;
int[] c;
if ( debug ) {
b = new int[2];
c = new int[2];
}
int wrap = blockWrap[blockNumber];
a = motion_val[ mot_val_offset - 1];
if ( !first_slice_line || blockNumber >= 3 ) {
/* Normal operation */
if ( debug ) System.out.println( "Normal " + (blockIndex[ blockNumber ]-82) );
b = motion_val[ mot_val_offset- wrap];
c = motion_val[ mot_val_offset + off[ blockNumber ] - wrap];
h263_predMotionX = mid_pred(a[0], b[0], c[0]);
h263_predMotionY = mid_pred(a[1], b[1], c[1]);
} else {
/* Edge cases */
if ( blockNumber == 0 ) {
if( mb_x == resync_mb_x ) {
h263_predMotionX = 0;
h263_predMotionY = 0;
} else if ( mb_x + 1 == resync_mb_x && h263_pred ) {
c = motion_val[ mot_val_offset + off[ blockNumber ] - wrap];
if ( mb_x == 0 ) {
h263_predMotionX = c[0];
h263_predMotionY = c[1];
} else {
h263_predMotionX = mid_pred(a[0], 0, c[0]);
h263_predMotionY = mid_pred(a[1], 0, c[1]);
}
} else {
h263_predMotionX = a[0];
h263_predMotionY = a[1];
}
} else if ( blockNumber == 1 ) {
if ( mb_x + 1 == resync_mb_x && h263_pred) {
c = motion_val[ mot_val_offset + off[ blockNumber ] - wrap];
h263_predMotionX = mid_pred(a[0], 0, c[0]);
h263_predMotionY = mid_pred(a[1], 0, c[1]);
}else{
h263_predMotionX = a[0];
h263_predMotionY = a[1];
}
} else {
/* blockNumber == 2 */
b = motion_val[ mot_val_offset - wrap];
c = motion_val[ mot_val_offset + off[ blockNumber ] - wrap];
if( mb_x == resync_mb_x) {
a[0] = 0;
a[1] = 0;
}
h263_predMotionX = mid_pred(a[0], b[0], c[0]);
h263_predMotionY = mid_pred(a[1], b[1], c[1]);
}
}
if ( debug ) System.out.println( "A:" + a[0] + " B:" + b[0] + " C:" + c[0] + " " + h263_predMotionX + " " + h263_predMotionY );
}
private VLCTable mv_vlc = new MVTable();
private int h263_decode_motion( int pred, int f_code ) throws FFMpegException {
int code = in.getVLC( mv_vlc );
if (code == 0)
return pred;
boolean sign = in.getTrueFalse();
int shift = f_code - 1;
int val = code;
if ( debug ) System.out.println( "Sign " + (sign?1:0) + " " + shift + " " + val );
if ( shift != 0 ) {
val = (val - 1) << shift;
val |= in.getBits(shift);
val++;
}
if (sign)
val = -val;
val += pred;
/* modulo decoding */
boolean h263_long_vectors = false;
if (!h263_long_vectors) {
int l = 32 - 5 - f_code;
val = ((val<<l)&0xffffffff)>>l;
} else {
/* horrible h263 long vector mode */
if (pred < -31 && val < -63)
val += 64;
if (pred > 32 && val > 63)
val -= 64;
}
return val;
}
private static final int ROUNDED_DIV( int a, int b ) {
return (((a)>0 ? (a) + ((b)>>1) : (a) - ((b)>>1))/(b));
}
private void mpeg4_pred_ac( int[] block, int n ) {
if ( debug ) System.out.println( "mpeg4_pred_ac" );
if(debug2) DisplayOutput.debug.println( "mpeg4_pred_ac" );
int i;
/* find prediction */
int ac_val_offset = blockIndex[n] * 16 + mbWidth * 16;
int ac_val_offset1 = ac_val_offset;
if (ac_pred) {
if (!dc_pred_dir) {
int xy= mb_x - 1 + mb_y * mbWidth -1; /*note extra -1 here */
/* left prediction */
ac_val_offset -= 16;
if(mb_x==0 || qscale == qscale_table[ xy+1] || n==1 || n==3 ) {
/* same qscale */
if (debug2) DisplayOutput.debug.println( "A" );
for(i=1;i<8;i++) {
block[ i<<3 ] += ac_val[ i + ac_val_offset ];
if ( debug2) DisplayOutput.debug.print( Integer.toHexString( block[ i<<3 ] )+ " " + Integer.toHexString(ac_val[ i + ac_val_offset ]) + " ");
}
} else {
/* different qscale, we must rescale */
if (debug2) DisplayOutput.debug.println( "B" );
for( i = 1; i < 8; i++ ) {
block[ i<<3 ] += ROUNDED_DIV(ac_val[ i + ac_val_offset ] * qscale_table[xy + 1], qscale);
if ( debug2 ) DisplayOutput.debug.print( Integer.toHexString( block[ i<<3 ] )+ " " + Integer.toHexString(ac_val[ i + ac_val_offset ]) + " " );
}
}
} else {
int xy= mb_x + (mb_y - 1)* mbWidth-1;
/* top prediction */
ac_val_offset -= 16 * blockWrap[n];
if ( mb_y == 0 || qscale == qscale_table[ xy+1] || n==2 || n==3) {
/* same qscale */
for( i = 1; i < 8; i++ ) {
block[i] += ac_val[ i + 8 + ac_val_offset ];
if (debug2) DisplayOutput.debug.print( Integer.toHexString( block[ i ] )+ " " );
}
} else {
/* different qscale, we must rescale */
for ( i = 1; i < 8; i++ ) {
block[i] += ROUNDED_DIV(ac_val[i + 8 + ac_val_offset ]*qscale_table[xy+1], qscale);
if (debug2 ) DisplayOutput.debug.print( Integer.toHexString( block[ i ] )+ " " );
}
}
}
}
/* left copy */
for( i = 1; i < 8; i++ ) {
ac_val[i + ac_val_offset1 ] = block[i<<3];
//DisplayOutput.debug.print( block[i<<3] + " " );
}
/* top copy */
for(i=1;i<8;i++) {
ac_val[8 + i + ac_val_offset1] = block[i];
//DisplayOutput.debug.print( block[i] + " " );
}
if (debug2 ) DisplayOutput.debug.println();
}
private VLCTable dc_lumTable = new dcLuminanceVlc();
private VLCTable dc_chromTable = new dcChrominanceVlc();
private int mpeg4_decode_dc( int n ) throws FFMpegException {
int level = 0;
int code;
if ( n < 4 ) {
code = in.getVLC( dc_lumTable );
} else {
code = in.getVLC( dc_chromTable );
}
if ( code != 0 ) {
/* get_xbits() */
level = in.getBits( code );
if ( (level & (1<< (code-1))) == 0 ) {
/* Negative (assume top bit) */
level = (-1<<code) | (level) + 1;
}
if ( code > 8 ) in.getTrueFalse();
}
if ( debug ) System.out.println( "l1 " + level );
/* ff_mpeg4_pred_dc */
int scale = (n<4)?y_dc_scale:c_dc_scale;
int dc_offset = blockIndex[n];
int wrap = blockWrap[n];
int a = dc_val[ dc_offset - 1 ];
int b = dc_val[ dc_offset - 1 - wrap ];
int c = dc_val[ dc_offset - wrap ];
if ( first_slice_line && n != 3 ) {
if ( n != 2 ) {
b = 1024;
c = 1024;
}
if ( n != 1 && mb_x == resync_mb_x ) {
a = 1024;
b = 1024;
}
}
if ( mb_x == resync_mb_x && mb_y == (resync_mb_y + 1) ) {
if ( n == 0 || n == 4 || n == 5 ) {
b = 1024;
}
}
if ( debug ) System.out.println( "offset " + (dc_offset-blockWrap[0]-1) + " a:" + a +" b:" + b + " c:" + c + " fsl " + (first_slice_line?1:0));
int pred;
if ( (a-b)*(a-b) < (b-c)*(b-c) ) {
pred = c;
dc_pred_dir = true;
} else {
pred = a;
dc_pred_dir = false;
}
pred = (pred + (scale>>1))/scale;
if ( debug ) System.out.println( "pred " + pred );
/* end ff_mpeg4_pred_dc */
level += pred;
if ( level < 0 ) level = 0;
dc_val[ dc_offset ] = level * ((n<4)?y_dc_scale:c_dc_scale);
return level;
}
private RLTable rvlc_rl_intra = new RLRVlcRLIntra();
private RLTable rl_intra = new RLIntra();
private RLTable rvlc_rl_inter = new RLRVlcRLInter();
private RLTable rl_inter = new RLInter();
private boolean dc_pred_dir;
private void mpeg4_decode_block( int[] block, int n, boolean coded, boolean intra ) throws FFMpegException {
boolean rvlc = false;
if ( debug ) System.out.println( "mpeg4_decode_block");
int[] scan_table = intra_scantable;
RLTable rl = null;
int level;
int i = 0;
int qmul = 1;
int qadd = 0;
if ( intra ) {
if(qscale < intra_dc_threshold){
if ( partitioned_frame ) {
level = dc_val[ blockIndex[ n ] ];
if ( n < 4 ) {
level = (level + (y_dc_scale>>1))/y_dc_scale;
} else {
level = (level + (c_dc_scale>>1))/c_dc_scale;
}
dc_pred_dir = (0 != ((pred_dir_table[ mb_x + mb_y * mb_stride ] << n) & 32));
} else {
level = mpeg4_decode_dc( n );
}
block[0] = level;
i = 0;
if ( debug ) System.out.println( "Level " + level );
} else {
i = -1;
}
if ( coded ) {
rl = rvlc ? rvlc_rl_intra : rl_intra;
if ( ac_pred ) {
if (!dc_pred_dir)
scan_table = intra_v_scantable;
else
scan_table = intra_h_scantable;
} else {
scan_table = intra_scantable;
}
qmul = 1;
qadd = 0;
}
} else {
if ( debug ) System.out.println( "Inter" );
i = -1;
if (!coded) {
block_last_index[ n ] = i;
return;
}
rl = rvlc ? rvlc_rl_inter : rl_inter;
scan_table = intra_scantable;
if(mpeg_quant) {
qmul=1;
qadd=0;
}else{
qmul = qscale << 1;
qadd = (qscale - 1) | 1;
}
}
/* Decode DCTELEM */
if ( debug ) if ( coded ) System.out.println( "Code " + Integer.toHexString( in.showBits(24) ) );
while ( coded ) {
int code = in.getVLC( rl );
level = rl.getLevel( code );
int run = rl.getRun( code );
boolean last = false;
if ( level == 0 ) {
if ( rvlc ) {
} else {
if ( in.getTrueFalse() ) {
if ( in.getTrueFalse() ) {
last = in.getTrueFalse();
run = in.getBits(6);
in.getTrueFalse();
level = in.getBits(12); //TODO signed
if ( (level & (1<<11)) != 0 ) {
level |= (-1)<<11;
}
in.getTrueFalse();
level = level * qmul + ((level > 0)?qadd:-qadd);
i += run + 1;
if ( last ) i += 192;
} else {
code = in.getVLC( rl );
level = rl.getLevel( code );
run = rl.getRun( code );
i += run + rl.getMaxRun()[run>>7][level] + 1;
level = level * qmul + qadd;
if ( in.getTrueFalse() ) level = -level;
}
} else {
code = in.getVLC( rl );
run = rl.getRun( code );
level = rl.getLevel( code ) * qmul + qadd;
i += run;
level = level + rl.getMaxLevel()[run>>7][(run-1)&63]*qmul;
if ( in.getTrueFalse() ) level = -level;
}
}
} else {
i += run;
level = level * qmul + qadd;
if ( in.getTrueFalse() ) level = -level;
}
// System.out.print( Integer.toHexString(level) + " " );
if ( debug ) System.out.println( "Level " + level + " run " + run + " i " + i);
if ( i > 62 ) {
i -= 192;
block[scan_table[i]] = level;
break;
}
block[scan_table[i]] = level;
}
/* Add prediction */
if ( mb_intra ) {
if( qscale >= intra_dc_threshold ) {
System.out.println( "TODO preddc" );
// block[0] = ff_mpeg4_pred_dc(s, n, block[0], &dc_pred_dir, 0);
if(i == -1) i=0;
}
// System.out.println( "predac" );
mpeg4_pred_ac( block, n );
if ( ac_pred ) i = 63;
}
block_last_index[ n ] = i;
// System.out.println();
}
private int get_amv( int i ) throws FFMpegException {
System.out.println( "get_amv " + i );
return i;
}
private boolean mb_intra;
private int mv_dir;
private int mv_type;
private boolean mcsel;
private boolean mb_skipped;
private int motion_pred_x;
private int motion_pred_y;
private boolean interlaced_dct;
private void ff_mpeg4_decode_mb() throws FFMpegException {
boolean mpeg4_decode_block_intra = false;
int cbp = 0;
int cbpc = 0;
int dquant = 0;
if ( debug ) System.out.println( "ff_mpeg4_decode_mb" );
int xy = mb_x + mb_y * mbWidth;
if ( pict_type == P_TYPE || pict_type == S_TYPE ) {
if ( debug ) System.out.println( "P/S_TYPE" );
do {
if ( in.getTrueFalse() ) {
mb_intra = false;
for ( int i = 0; i < NUMBER_OF_BLOCKS; i++ ) {
block_last_index[i] = -1;
}
mv_dir = MV_DIR_FORWARD;
mv_type = MV_TYPE_16X16;
if ( pict_type == S_TYPE && vol_sprite_usage == GMC_SPRITE ) {
mb_type[xy] =MB_TYPE_SKIP | MB_TYPE_GMC | MB_TYPE_16x16 | MB_TYPE_L0;
mcsel = true;
mv[0][0][0] = get_amv(0);
mv[0][0][1] = get_amv(1);
mb_skipped = false;
} else {
mb_type[xy] =MB_TYPE_SKIP | MB_TYPE_16x16 | MB_TYPE_L0;
mcsel = false;
mv[0][0][0] = 0;
mv[0][0][1] = 0;
mb_skipped = true;
}
//System.out.println( "Go to end" ); //TODO mpeg4_resync
return;
// throw new Error( "Code goto end" );
}
cbpc = in.getVLC( inter_MCBPC );
//System.out.println( "CBPC: " + cbpc);
} while ( cbpc == 20 );
dquant = cbpc & 8;
mb_intra = (cbpc & 4) != 0;
if ( !mb_intra ) { //REM goto intra:
int mx = 0, my = 0;
//System.out.println( "No goto intra" );
mcsel = false;
if( pict_type == S_TYPE
&& vol_sprite_usage == GMC_SPRITE
&& (cbpc & 16) == 0 ) {
mcsel = in.getTrueFalse();
}
int cbpy = in.getVLC( cbpyVlc )^0xf;
if ( debug ) System.out.println( "cbpy " + cbpy );
cbp = (cbpc & 3) | (cbpy << 2);
if ( dquant != 0 ) {
int[] quant_tab = new int[] { -1, -2, 1, 2 };
ff_set_qscale( qscale + quant_tab[ in.getBits(2) ] );
}
if(!progressive_sequence && cbp != 0) {
boolean interlaced_dct= in.getTrueFalse();
}
mv_dir = MV_DIR_FORWARD;
if ((cbpc & 16) == 0) {
if(mcsel){
if ( debug ) System.out.println( "Motion 1" );
mb_type[xy]= MB_TYPE_GMC | MB_TYPE_16x16 | MB_TYPE_L0;
/* 16x16 global motion prediction */
mv_type = MV_TYPE_16X16;
mx= get_amv(0);
my= get_amv(1);
mv[0][0][0] = mx;
mv[0][0][1] = my;
if ( debug ) System.out.println( "mx: " + mx + " my:" + my );
} else if((!progressive_sequence) && in.getTrueFalse() ){
if ( debug ) System.out.println( "Motion 2" );
mb_type[xy]= MB_TYPE_16x8|MB_TYPE_L0|MB_TYPE_INTERLACED;
/* 16x8 field motion prediction */
mv_type= MV_TYPE_FIELD;
boolean[][] field_select = new boolean[ 2 ][ 2 ];
field_select[0][0] = in.getTrueFalse();
field_select[0][1] = in.getTrueFalse();
h263_pred_motion(0);
for(int i=0; i<2; i++){
mx = h263_decode_motion(h263_predMotionX, f_code);
my = h263_decode_motion(h263_predMotionY/2, f_code);
mv[0][i][0] = mx;
mv[0][i][1] = my;
if ( debug ) System.out.println( "mx: " + mx + " my:" + my );
}
} else {
if ( debug ) System.out.println( "Motion 3" );
mb_type[xy]= MB_TYPE_16x16 | MB_TYPE_L0;
/* 16x16 motion prediction */
mv_type = MV_TYPE_16X16;
h263_pred_motion( 0 );
mx = h263_decode_motion(h263_predMotionX, f_code);
my = h263_decode_motion(h263_predMotionY, f_code);
mv[0][0][0] = mx;
mv[0][0][1] = my;
if ( debug ) System.out.println( "mx: " + mx + " my:" + my );
}
} else {
if ( debug ) System.out.println( "Motion 4" );
mb_type[xy]= MB_TYPE_8x8 | MB_TYPE_L0;
mv_type = MV_TYPE_8X8;
for( int i = 0; i < 4; i++ ) {
h263_pred_motion( i );
mx = h263_decode_motion(h263_predMotionX, f_code);
my = h263_decode_motion(h263_predMotionY, f_code);
mv[0][i][0] = mx;
mv[0][i][1] = my;
motion_val[ blockIndex[ i ] ][0] = mx;
motion_val[ blockIndex[ i ] ][1] = my;
if ( debug ) System.out.println( "8X8 " + (blockIndex[i]-82) + ", mx: " + mx + " my:" + my );
}
}
//System.out.println( "mx " + mx + " my " + my );
}
} else if ( pict_type == B_TYPE ) {
/* BFrames in XVID only */
if ( debug ) System.out.println( "B_TYPE" );
mb_intra = false;
int mx = 0;
int my = 0;
if( mb_x == 0 ) {
for( int i=0; i < 2; i++ ) {
last_mv[i][0][0]= 0;
last_mv[i][0][1]= 0;
last_mv[i][1][0]= 0;
last_mv[i][1][1]= 0;
}
}
/* if we skipped it in the future P Frame than skip it now too */
if( mb_skiptable[ mb_x ][ mb_y ] ) {
/* skip mb */
mv_dir = MV_DIR_FORWARD;
mv_type = MV_TYPE_16X16;
mv[0][0][0] = 0;
mv[0][0][1] = 0;
mv[1][0][0] = 0;
mv[1][0][1] = 0;
mb_type[xy]= MB_TYPE_SKIP | MB_TYPE_16x16 | MB_TYPE_L0;
if ( debug ) System.out.println( "BSKIP" );
return;
}
boolean modb1 = in.getTrueFalse();
if( modb1 ) {
mb_type[xy] = MB_TYPE_DIRECT2 | MB_TYPE_SKIP | MB_TYPE_L0L1;
cbp=0;
} else {
boolean modb2 = in.getTrueFalse();
mb_type[xy] = in.getVLC( mb_type_b );
mb_type[xy] = mb_type_b_map[ mb_type[xy] ];
if ( modb2 ) {
cbp= 0;
} else {
cbp= in.getBits( 6 );
}
if ( ((IS_DIRECT_MASK & mb_type[xy]) == 0) && cbp != 0 ) {
if ( in.getTrueFalse() ) {
ff_set_qscale( qscale + in.getBits( 1 ) * 4 - 2);
}
}
if ( !progressive_sequence ) {
if ( cbp != 0 ) {
interlaced_dct= in.getTrueFalse();
}
if( ((IS_DIRECT_MASK & mb_type[xy]) == 0) && in.getTrueFalse() ) {
mb_type[xy] |= MB_TYPE_16x8 | MB_TYPE_INTERLACED;
mb_type[xy] &= ~MB_TYPE_16x16;
if(USES_LIST(mb_type[xy], 0)){
field_select[0][0]= in.getBits(1);
field_select[0][1]= in.getBits(1);
}
if(USES_LIST(mb_type[xy], 1)){
field_select[1][0]= in.getBits(1);
field_select[1][1]= in.getBits(1);
}
}
}
mv_dir = 0;
if ( (mb_type[xy] & (MB_TYPE_DIRECT2|MB_TYPE_INTERLACED)) == 0 ) {
mv_type = MV_TYPE_16X16;
if ( USES_LIST(mb_type[xy], 0) ) {
mv_dir = MV_DIR_FORWARD;
if (debug) System.out.println( "BMotion 1" );
mx = h263_decode_motion( last_mv[0][0][0], f_code );
my = h263_decode_motion( last_mv[0][0][1], f_code );
last_mv[0][1][0] = mx;
last_mv[0][0][0] = mx;
mv[0][0][0] = mx;
last_mv[0][1][1] = my;
last_mv[0][0][1] = my;
mv[0][0][1] = my;
}
if ( USES_LIST(mb_type[xy], 1)){
mv_dir |= MV_DIR_BACKWARD;
if (debug) System.out.println( "BMotion 2" );
mx = h263_decode_motion( last_mv[1][0][0], b_code );
my = h263_decode_motion( last_mv[1][0][1], b_code );
last_mv[1][1][0] = mx;
last_mv[1][0][0] = mx;
mv[1][0][0] = mx;
last_mv[1][1][1] = my;
last_mv[1][0][1] = my;
mv[1][0][1] = my;
}
} else if( ((IS_DIRECT_MASK & mb_type[xy]) == 0) ) {
mv_type= MV_TYPE_FIELD;
if ( USES_LIST(mb_type[xy], 0) ) {
mv_dir = MV_DIR_FORWARD;
if (debug) System.out.println( "BMotion 3" );
for( int i = 0; i < 2; i++ ) {
mx = h263_decode_motion(last_mv[0][i][0], f_code);
my = h263_decode_motion(last_mv[0][i][1]/2, f_code);
last_mv[0][i][0] = mx;
mv[0][i][0] = mx;
last_mv[0][i][1] = my * 2;
mv[0][i][1] = my;
}
}
if ( USES_LIST(mb_type[xy], 1 ) ) {
mv_dir |= MV_DIR_BACKWARD;
if (debug) System.out.println( "BMotion 4" );
for( int i = 0; i < 2; i++ ) {
mx = h263_decode_motion(last_mv[1][i][0], b_code);
my = h263_decode_motion(last_mv[1][i][1]/2, b_code);
last_mv[1][i][0] = mx;
mv[1][i][0] = mx;
last_mv[1][i][1] = my * 2;
mv[1][i][1] = my;
}
}
}
}
if ( debug ) System.out.println( "mb_type " + mb_type[xy] );
if( ((IS_DIRECT_MASK & mb_type[xy]) != 0) ){
if( (IS_SKIP_MASK & mb_type[xy]) != 0 ) {
mx = 0;
my = 0;
} else {
if (debug) System.out.println( "BMotion 5" );
mx = h263_decode_motion(0, 1);
my = h263_decode_motion(0, 1);
}
mv_dir = MV_DIR_FORWARD | MV_DIR_BACKWARD | MV_DIRECT;
mb_type[xy] |= ff_mpeg4_set_direct_mv(mx, my);
}
//System.out.println( "mx " + mx + " my " + my );
} else {
if ( debug ) System.out.println( "I_TYPE" );
do {
cbpc = in.getVLC( intra_MCBPC );
} while ( cbpc == 8 );
dquant = cbpc & 4;
mb_intra = true;
}
/* intra: */
if ( mb_intra ) {
ac_pred = in.getTrueFalse();
mb_type[ xy ] = MB_TYPE_INTRA;
if ( ac_pred ) {
mb_type[ xy ] = MB_TYPE_INTRA | MB_TYPE_ACPRED;
}
if ( debug ) System.out.println( "cbpc " + cbpc );
int cbpy = in.getVLC( cbpyVlc );
if ( debug ) System.out.println( "cbpy " + cbpy );
cbp = (cbpc & 3) | (cbpy << 2);
if ( dquant != 0 ) {
ff_set_qscale( qscale + quant_tab[ in.getBits(2) ] );
}
if(!progressive_sequence) {
interlaced_dct= in.getTrueFalse();
}
mpeg4_decode_block_intra = true;
}
for ( int i = 0; i < NUMBER_OF_BLOCKS; i++ ) {
mpeg4_decode_block(block[i], i, (cbp & 32) != 0, mpeg4_decode_block_intra);
cbp <<= 1;
}
/* End of slice */
}
int[] quant_tab = new int[] { -1, -2, 1, 2 };
private int ff_mpeg4_set_direct_mv( int mx, int my ) {
int time_pb = pb_time;
int time_pp = pp_time;
int mb_index = mb_x + mb_y * mbWidth;
int colocated_mb_type = mb_type_ip_frame[mb_index];
int xy = blockIndex[0];
if ( debug) System.out.println( "colocated_mb_type " + colocated_mb_type );
if ( debug ) System.out.println( "set_direct" );
next_motion_val = motion_val; /* This works as this function is only called in BFrames */
if( (IS_8X8_MASK & colocated_mb_type) != 0 ) {
mv_type = MV_TYPE_8X8;
for( int i = 0; i < 4; i++ ) {
xy = blockIndex[i];
mv[0][i][0] = (next_motion_val[xy][0] * time_pb)/time_pp + mx;
mv[0][i][1] = (next_motion_val[xy][1] * time_pb)/time_pp + my;
mv[1][i][0] = (mx == 0) ? (mv[0][i][0] - next_motion_val[xy][0])
: next_motion_val[xy][0]* (time_pb - time_pp) / time_pp;
mv[1][i][1] = (my == 0) ? (mv[0][i][1] - next_motion_val[xy][1])
: next_motion_val[xy][1]* (time_pb - time_pp) / time_pp;
}
return MB_TYPE_DIRECT2 | MB_TYPE_8x8 | MB_TYPE_L0L1;
} else if ( (IS_INTERLACED_MASK & colocated_mb_type) != 0 ) {
/* TODO Code me *
mv_type = MV_TYPE_FIELD;
for ( int i = 0; i < 2; i++ ) {
int field_select= s->next_picture.ref_index[0][s->block_index[2*i]];
if(s->top_field_first){
time_pp= s->pp_field_time - field_select + i;
time_pb= s->pb_field_time - field_select + i;
}else{
time_pp= s->pp_field_time + field_select - i;
time_pb= s->pb_field_time + field_select - i;
}
s->mv[0][i][0] = s->p_field_mv_table[i][0][mb_index][0]*time_pb/time_pp + mx;
s->mv[0][i][1] = s->p_field_mv_table[i][0][mb_index][1]*time_pb/time_pp + my;
s->mv[1][i][0] = mx ? s->mv[0][i][0] - s->p_field_mv_table[i][0][mb_index][0]
: s->p_field_mv_table[i][0][mb_index][0]*(time_pb - time_pp)/time_pp;
s->mv[1][i][1] = my ? s->mv[0][i][1] - s->p_field_mv_table[i][0][mb_index][1]
: s->p_field_mv_table[i][0][mb_index][1]*(time_pb - time_pp)/time_pp;
}
*/
System.out.println( "TODO: IS_INTERLACED" );
return MB_TYPE_DIRECT2 | MB_TYPE_16x8 | MB_TYPE_L0L1 | MB_TYPE_INTERLACED;
} else {
int t = (next_motion_val[xy][0] * time_pb)/time_pp + mx;
mv[0][0][0] = t;
mv[0][1][0] = t;
mv[0][2][0] = t;
mv[0][3][0] = t;
t = (next_motion_val[xy][1] * time_pb)/time_pp + my;
mv[0][0][1] = t;
mv[0][1][1] = t;
mv[0][2][1] = t;
mv[0][3][1] = t;
t = (mx != 0) ? mv[0][0][0] - next_motion_val[xy][0]
: next_motion_val[xy][0]*(time_pb - time_pp)/time_pp;
mv[1][0][0] = t;
mv[1][1][0] = t;
mv[1][2][0] = t;
mv[1][3][0] = t;
t = (my != 0) ? mv[0][0][1] - next_motion_val[xy][1]
: next_motion_val[xy][1] * (time_pb - time_pp)/time_pp;
mv[1][0][1] = t;
mv[1][1][1] = t;
mv[1][2][1] = t;
mv[1][3][1] = t;
/*System.out.println( "direct " + mv[0][0][0] + " " + mv[0][0][1] + " "
+ mv[1][0][0] + " " + mv[1][0][1] + " " + next_motion_val[xy][0] + " "
+ mx + " " + my + " " + time_pb + " " + time_pp );
*/
if( !quarter_sample ) {
mv_type= MV_TYPE_16X16;
} else {
mv_type= MV_TYPE_8X8;
}
return MB_TYPE_DIRECT2 | MB_TYPE_16x16 | MB_TYPE_L0L1; //Note see prev line
}
}
/**
* set qscale and update qscale dependant variables.
*/
private void ff_set_qscale(int qscale) {
if (qscale < 1)
qscale = 1;
else if (qscale > 31)
qscale = 31;
this.qscale = qscale;
int chroma_qscale = chroma_qscale_table[qscale];
y_dc_scale= ff_mpeg4_y_dc_scale_table[ qscale ];
c_dc_scale= ff_mpeg4_c_dc_scale_table[ chroma_qscale ];
}
private int[] chroma_qscale_table = ff_default_chroma_qscale_table;
private static int[] ff_default_chroma_qscale_table = new int[] {
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
};
private VLCTable inter_MCBPC = new InterMcbpc();
private VLCTable intra_MCBPC = new IntraMcbpc();
private VLCTable cbpyVlc = new CbpyVlc();
private static final int IS_INTRA_MASK = 7;
private static final int IS_DIRECT_MASK = 0x100;
private static final int IS_SKIP_MASK = 0x800;
private static final int IS_8X8_MASK = 0x40;
private static final int IS_INTERLACED_MASK = 0x80;
private static final int MB_TYPE_DIRECT2 = 0x100;
private static final int MB_TYPE_ACPRED = 0x200;
private static final int MB_TYPE_GMC = 0x400;
private static final int MB_TYPE_SKIP = 0x800;
private static final int MB_TYPE_INTRA = 0x1;
private static final int MB_TYPE_INTERLACED = 0x80;
private static final int MB_TYPE_8x8 = 0x40;
private static final int MB_TYPE_16x8 = 0x10;
private static final int MB_TYPE_16x16 = 0x8;
private static final int MB_TYPE_P0L0 = 0x1000;
private static final int MB_TYPE_P1L0 = 0x2000;
private static final int MB_TYPE_P0L1 = 0x4000;
private static final int MB_TYPE_P1L1 = 0x8000;
private static final int MB_TYPE_L0 = MB_TYPE_P0L0|MB_TYPE_P1L0;
private static final int MB_TYPE_L1 = MB_TYPE_P0L1 | MB_TYPE_P1L1;
private static final int MB_TYPE_L0L1 = MB_TYPE_L0 | MB_TYPE_L1;
private static final int MV_TYPE_FIELD = 3;
private static final int MV_DIR_FORWARD = 2;
private static final int MV_DIR_BACKWARD = 1;
private static final int MV_DIRECT = 4;
private static final int MV_TYPE_8X8 = 1;
private static final int MV_TYPE_16X16 = 0;
private static final boolean USES_LIST( int a, int list) {
return ((a) & ((MB_TYPE_P0L0|MB_TYPE_P1L0)<<(2*(list)))) != 0;
///< does this mb use listX, note doesnt work if subMBs
}
/* XVID only */
private VLCTable mb_type_b = new MbTypeBTable();
private static final int[] mb_type_b_map = new int[] {
MB_TYPE_DIRECT2 | MB_TYPE_L0L1,
MB_TYPE_L0L1 | MB_TYPE_16x16,
MB_TYPE_L1 | MB_TYPE_16x16,
MB_TYPE_L0 | MB_TYPE_16x16,
};
private int[][][] mv = new int[ 2 ][ 4 ][ 2 ];
private int[][][] last_mv = new int[ 2 ][ 4 ][ 2 ];
private boolean ac_pred;
private void mpeg4_decode_partitioned_mb() throws FFMpegException {
if ( debug ) System.out.println( "mpeg4_decode_partitioned_mb" );
int mv_dir, mv_type;
int xy = mb_x + mb_y * mbWidth;
int mb_type = this.mb_type[ xy ];
int cbp = cbp_table[ xy ];
if ( qscale != qscale_table[ xy ] ) {
ff_set_qscale( qscale_table[ xy ] );
}
if ( pict_type == P_TYPE || pict_type == S_TYPE ) {
for ( int i = 0; i < 4; i++ ) {
mv[0][i][0] = motion_val[ blockIndex[i] ][0];
mv[0][i][1] = motion_val[ blockIndex[i] ][1];
}
mb_intra = (IS_INTRA_MASK & mb_type) != 0;
boolean isSkip = (MB_TYPE_SKIP & mb_type) != 0;
if ( isSkip ) {
for ( int i = 0; i < NUMBER_OF_BLOCKS; i++ ) {
block_last_index[i] = -1;
}
mv_dir = MV_DIR_FORWARD;
mv_type = MV_TYPE_16X16;
boolean mcsel = false;
mb_skipped = true;
if ( pict_type == S_TYPE && vol_sprite_usage == GMC_SPRITE ) {
mcsel = true;
mb_skipped = false;
}
} else if ( mb_intra ) {
ac_pred = (MB_TYPE_ACPRED & mb_type)!= 0;
/* Not relevant */
mv_type = MV_TYPE_16X16;
} else if ( !mb_intra ) {
mv_dir = MV_DIR_FORWARD;
mv_type = MV_TYPE_16X16;
if ( (MB_TYPE_8x8 & mb_type) != 0 ) {
mv_type = MV_TYPE_8X8;
if ( debug ) System.out.println( "MV_TYPE_8X8" );
}
}
} else {
/* IFRAME */
mb_intra = true;
ac_pred = (MB_TYPE_ACPRED & mb_type) != 0;
}
boolean isSkip = (MB_TYPE_SKIP & mb_type) != 0;
if ( !isSkip ) {
for ( int i = 0; i < 6; i++ ) {
// mpeg4_decode_block( block[i], i, cbp & 32, mb_intra, rvlc );
cbp <<= 1;
}
}
/* TODO Slice end prediction */
}
private int[] ff_mpeg4_y_dc_scale_table = new int[] {
0, 8, 8, 8, 8,10,12,14,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,31,32,34,36,38,40,42,44,46
};
private int[] ff_mpeg4_c_dc_scale_table = new int[] {
0, 8, 8, 8, 8, 9, 9,10,10,11,11,12,12,
13,13,14,14,15,15,16,16,17,17,18,18,19,20,21,22,23,24,25
};
private static final int NUMBER_OF_BLOCKS = 6;
private int[] blockWrap = new int[ NUMBER_OF_BLOCKS ];
private int[] blockIndex = new int[ NUMBER_OF_BLOCKS ];
private int[][] block = new int[ NUMBER_OF_BLOCKS ][ 64 ];
private int[] block_last_index = new int[ NUMBER_OF_BLOCKS ];
private int[] blank = new int[ 64 ];
private int y_dc_scale;
private int c_dc_scale;
private boolean first_slice_line;
private int resync_mb_x;
private int resync_mb_y;
private void decode_slice() throws FFMpegException {
int last_resync_gb = in.getPos();
first_slice_line = true;
resync_mb_x = mb_x;
resync_mb_y = mb_y;
/* TODO chose tables dynamically? */
ff_set_qscale( qscale );
if ( debug ) System.out.println( "y_dc_scale " + y_dc_scale );
if ( debug ) System.out.println( "c_dc_scale " + c_dc_scale );
for ( ; mb_y < mbHeight; mb_y++ ) {
/**
* Initialise the quick lookup arrays
*/
blockIndex[ 0 ] = blockWrap[ 0 ] * ( mb_y * 2 + 1 ) - 1;
blockIndex[ 1 ] = blockIndex[ 0 ] + 1;
blockIndex[ 2 ] = blockIndex[ 0 ] + blockWrap[ 0 ];
blockIndex[ 3 ] = blockIndex[ 2 ] + 1;
blockIndex[ 4 ] = blockWrap[ 0 ] * ( mbHeight * 2 + 1)
+ blockWrap[ 4 ] * ( mb_y + 1 );
blockIndex[ 5 ] = blockIndex[ 4 ] + blockWrap[ 4 ] * (mbHeight + 1);
for ( ; mb_x < mbWidth; mb_x++ ) {
if ( debug ) System.out.println( "XY" + mb_x + "," + mb_y );
/**
* Increment block index
*/
blockIndex[ 0 ] += 2;
blockIndex[ 1 ] += 2;
blockIndex[ 2 ] += 2;
blockIndex[ 3 ] += 2;
blockIndex[ 4 ]++;
blockIndex[ 5 ]++;
if ( resync_mb_x == mb_x
&& resync_mb_y == mb_y - 1 ) first_slice_line = false;
/**
* Clear blocks
*/
for ( int i = 0; i < NUMBER_OF_BLOCKS; i++ ) {
System.arraycopy( blank, 0, block[i], 0, blank.length );
}
int mv_dir = 0; //MV_DIR_FORWARD
int mv_type = 0; //MV_TYPE_16X16
if ( decode_mb == 0 ) {
//ff_h263_decode_mb
ff_mpeg4_decode_mb();
// throw new FFMpegException( "codeme" );
} else {
mpeg4_decode_partitioned_mb();
}
MPV_decode_mb( mb_x, mb_y );
}
mb_x = 0;
}
if ( debug ) System.out.println( "End of Screen" );
}
private int vo_type;
private int vo_ver_id;
private int shape;
private static int FF_ASPECT_EXTENDED = 15;
private static final int RECT_SHAPE = 0;
private static final int BIN_ONLY_SHAPE = 2;
private static final int GRAY_SHAPE = 3;
private static final int STATIC_SPRITE = 1;
private static final int GMC_SPRITE = 2;
private int[] intra_matrix = new int[ 64 ];
private int[] chroma_intra_matrix = new int[ 64 ];
private int[] inter_matrix = new int[ 64 ];
private int[] chroma_inter_matrix = new int[ 64 ];
private boolean data_partitioning;
private int time_increment_resolution;
private int time_increment_bits;
private boolean vol_control_parameters;
private int quant_precision;
private int qscale;
private boolean mpeg_quant;
private void decode_vol_header() throws FFMpegException {
if ( debug ) System.out.println( "decode_vol_header" );
in.getTrueFalse();
vo_type = in.getBits( 8 );
vo_ver_id = 1;
if ( in.getTrueFalse() ) {
vo_ver_id = in.getBits( 4 );
in.getBits( 3 );
}
int aspect_ratio_info = in.getBits( 4 );
if ( aspect_ratio_info == FF_ASPECT_EXTENDED ) {
int aspected_width = in.getBits(8);
int aspected_height = in.getBits(8);
} else {
/* TODO lookup table */
}
vol_control_parameters = in.getTrueFalse();
if ( vol_control_parameters ) {
int chroma_format = in.getBits( 2 );
if ( chroma_format != 1 ) throw new FFMpegException( "Illegal Chroma" );
boolean low_delay = in.getTrueFalse();
if ( in.getTrueFalse() ) {
in.getBits( 15 );
in.getTrueFalse();
in.getBits( 15 );
in.getTrueFalse();
in.getBits( 15 );
in.getTrueFalse();
in.getBits( 3 );
in.getBits( 11 );
in.getTrueFalse();
in.getBits( 15 );
in.getTrueFalse();
}
} else {
int low_delay = 0;
}
shape = in.getBits( 2 );
if ( shape != RECT_SHAPE ) if ( debug ) System.out.println( "Not RECT_SHAPE" );
if ( shape == GRAY_SHAPE ) {
in.getBits(4);
}
if ( debug ) System.out.println( "shape: " + shape );
in.getTrueFalse();
time_increment_resolution = in.getBits(16);
if ( debug ) System.out.println( "time_increment_resolution: " + time_increment_resolution );
time_increment_bits = av_log2( time_increment_resolution - 1 );
if ( debug ) System.out.println( "time_increment_bits: " + time_increment_bits );
if ( time_increment_bits < 1 ) time_increment_bits = 1;
in.getTrueFalse();
if ( in.getTrueFalse() ) in.getBits( time_increment_bits );
if ( shape != BIN_ONLY_SHAPE ) {
if ( shape == RECT_SHAPE ) {
in.getTrueFalse();
int w = in.getBits(13);
in.getTrueFalse();
int h = in.getBits(13);
in.getTrueFalse();
if ( w != 0 && h != 0 ) {
width = w;
height = h;
}
}
progressive_sequence = !in.getTrueFalse();
in.getTrueFalse();
vol_sprite_usage = in.getBits( vo_ver_id == 1 ? 1:2 );
if ( vol_sprite_usage == STATIC_SPRITE
|| vol_sprite_usage == GMC_SPRITE ) {
if ( vol_sprite_usage == STATIC_SPRITE ) {
int sprite_width = in.getBits(13);
in.getTrueFalse();
int sprite_height = in.getBits(13);
in.getTrueFalse();
int sprite_left = in.getBits(13);
in.getTrueFalse();
int sprite_top = in.getBits(13);
in.getTrueFalse();
}
num_sprite_warping_points = in.getBits(6);
sprite_warping_accuracy = in.getBits(2);
boolean brightness_change = in.getTrueFalse();
if ( vol_sprite_usage == STATIC_SPRITE ) {
boolean low_latency_sprite = in.getTrueFalse();
}
}
quant_precision = 5;
if ( in.getTrueFalse() ) {
quant_precision = in.getBits(4);
in.getBits(4);
}
if ( debug ) System.out.println( "quant_precision: " + quant_precision );
mpeg_quant = in.getTrueFalse();
if ( mpeg_quant ) {
/* Default matricies */
for ( int i = 0; i < 64; i++ ) {
intra_matrix[i] = Tables.ff_mpeg4_default_intra_matrix[i];
chroma_intra_matrix[i] = Tables.ff_mpeg4_default_intra_matrix[i];
inter_matrix[i] = Tables.ff_mpeg4_default_non_intra_matrix[i];
chroma_inter_matrix[i] = Tables.ff_mpeg4_default_non_intra_matrix[i];
}
if ( in.getTrueFalse() ) {
int last = 0;
int i = 0;
for ( ; i < 64; i++ ) {
int v = in.getBits( 8 );
if ( v == 0 ) break;
last = v;
intra_matrix[ i ] = v;
chroma_intra_matrix[i ] = v;
}
for ( ; i < 64; i++ ) {
intra_matrix[ i ] = last;
chroma_intra_matrix[i ] = last;
}
}
if ( in.getTrueFalse() ) {
int last = 0;
int i = 0;
for ( ; i < 64; i++ ) {
int v = in.getBits( 8 );
if ( v == 0 ) break;
last = v;
inter_matrix[ i ] = v;
chroma_inter_matrix[i ] = v;
}
for ( ; i < 64; i++ ) {
inter_matrix[ i ] = last;
chroma_inter_matrix[i ] = last;
}
}
}
quarter_sample = false;
if ( vo_ver_id != 1) quarter_sample = in.getTrueFalse();
in.getTrueFalse();
resync_marker = !in.getTrueFalse();
data_partitioning = in.getTrueFalse();
if ( data_partitioning ) {
boolean rvlc = in.getTrueFalse();
}
boolean new_pred = false;
boolean reduced_res_vop = false;
if ( vo_ver_id != 1 ) {
new_pred = in.getTrueFalse();
if (new_pred) {
in.getBits(3); //Not supported
}
reduced_res_vop = in.getTrueFalse();
}
scalability = in.getTrueFalse();
if ( scalability ) {
boolean hierarchy_type = in.getTrueFalse();
int ref_layer_id = in.getBits(4);
boolean ref_layer_sampling_dir = in.getTrueFalse();
int h_sampling_factor_n = in.getBits(5);
int h_sampling_factor_m = in.getBits(5);
int v_sampling_factor_n = in.getBits(5);
int v_sampling_factor_m = in.getBits(5);
enhancement_type = in.getTrueFalse();
}
}
if ( debug ) System.out.println( "decode_vol_header " + Integer.toHexString( in.showBits( 24 ) ) );
}
private static final int av_log2( int a ) {
int n = 0;
while ( a != 0 ) {
a = (a >> 1) &0xffff;
n++;
}
return n;
}
private void decode_user_data() throws FFMpegException {
char[] buffer = new char[ 256 ];
int i;
buffer[0] = (char)in.getBits(8);
for ( i = 1; i < 256; i++ ) {
buffer[i] = (char)in.showBits(8);
if ( buffer[i] == 0 ) break;
in.getBits(8);
}
String version = new String( buffer, 0, i );
if ( debug ) System.out.println( "User data: " + version );
}
private void mpeg4_decode_gop_header() throws FFMpegException {
int hours = in.getBits(5);
int minutes = in.getBits(6);
in.getTrueFalse();
int seconds = in.getBits(6);
in.getTrueFalse();
in.getTrueFalse();
if ( debug ) System.out.println( "" + hours + ":" + minutes + ":" + seconds );
}
private static int I_TYPE = 1;
private static int P_TYPE = 2;
private static int B_TYPE = 3;
private static int S_TYPE = 4;
private boolean progressive_sequence;
private boolean alternate_scan;
private int vol_sprite_usage;
private boolean scalability;
private boolean enhancement_type;
private boolean top_field_first;
private boolean quarter_sample;
private boolean resync_marker;
private int num_sprite_warping_points;
private int sprite_warping_accuracy;
private int intra_dc_threshold = 0;
private int decode_mb;
private int pict_type;
private boolean partitioned_frame;
private boolean no_rounding;
private int f_code;
private int b_code;
private int[][] field_select = new int[2][2];
private int time_base;
private int last_time_base;
private int last_non_b_time;
private int pp_time;
private int pb_time;
private int[] inter_scantable;
private int[] intra_scantable;
private int[] intra_h_scantable;
private int[] intra_v_scantable;
private static final int[] ff_zigzag_direct = ScanTable.getZigZagDirectTable();
private static final int[] ff_alternate_horizontal_scan = ScanTable.getAlternativeHScanTable();
private static final int[] ff_alternate_vertical_scan = ScanTable.getAlternativeVScanTable();
private static final int[] mpeg4_dc_threshold = new int[] {
99, 13, 15, 17, 19, 21, 23, 0
};
/* Returns true for full header */
private boolean decode_vop_header() throws FFMpegException {
if ( debug ) System.out.println( "decode_vop_header " + Integer.toHexString( in.showBits( 24 ) ) );
pict_type = in.getBits(2) + I_TYPE;
if ( debug ) System.out.println( "pict_type " + pict_type );
partitioned_frame = data_partitioning & (pict_type != B_TYPE);
decode_mb = partitioned_frame ? 1:0;
if (time_increment_resolution == 0) {
time_increment_resolution = 1;
}
int time_incr = 0;
while (in.getTrueFalse()) time_incr++;
if ( !in.getTrueFalse() ) throw new FFMpegException( "before_time_incr" );
if ( debug ) System.out.println( "time_incr " + time_incr );
int time_increment = in.getBits( time_increment_bits );
if ( pict_type != B_TYPE ) {
last_time_base = time_base;
time_base += time_incr;
int time = time_base * time_increment_resolution + time_increment;
pp_time = time - last_non_b_time;
//System.out.println( "pp_time " + pp_time + " " + time_base + " " + time_increment_resolution + " " + time_increment );
last_non_b_time = time;
} else {
/* TODO Time stuff */
int time= (last_time_base + time_incr)*time_increment_resolution + time_increment;
pb_time = pp_time - (last_non_b_time - time);
}
if ( debug ) System.out.println( "time_increment_bits " + time_increment_bits );
if ( debug ) System.out.println( "time_increment " + time_increment );
if ( !in.getTrueFalse() ) throw new FFMpegException( "before_vop_coded" );
if ( !in.getTrueFalse() ) return false;
no_rounding = false;
if (shape != BIN_ONLY_SHAPE
&& ( pict_type == P_TYPE || (pict_type == S_TYPE && vol_sprite_usage==GMC_SPRITE ) ) ) {
no_rounding = in.getTrueFalse();
}
if ( shape != RECT_SHAPE ) {
if ( vol_sprite_usage != 1 || pict_type != I_TYPE ) {
int width = in.getBits(13);
in.getTrueFalse();
int height = in.getBits(13);
in.getTrueFalse();
int hor_spat_ref = in.getBits(13);
in.getTrueFalse();
int ver_spat_ref = in.getBits(13);
}
in.getTrueFalse();
if( in.getTrueFalse() ) {
in.getBits(8);
}
}
if ( shape != BIN_ONLY_SHAPE ) {
int t = in.getBits(3);
intra_dc_threshold = mpeg4_dc_threshold[ t ];
alternate_scan = false;
if ( !progressive_sequence ) {
top_field_first = in.getTrueFalse();
alternate_scan = in.getTrueFalse();
}
}
if ( alternate_scan ) {
/* inter_scantable */
inter_scantable = ff_alternate_vertical_scan;
intra_scantable = ff_alternate_vertical_scan;
intra_h_scantable = ff_alternate_vertical_scan;
intra_v_scantable = ff_alternate_vertical_scan;
} else {
inter_scantable = ff_zigzag_direct;
intra_scantable = ff_zigzag_direct;
intra_h_scantable = ff_alternate_horizontal_scan;
intra_v_scantable = ff_alternate_vertical_scan;
}
if ( pict_type == S_TYPE
&& (vol_sprite_usage == STATIC_SPRITE || vol_sprite_usage==GMC_SPRITE) ) {
// mpeg4_decode_sprite_trajectory();
throw new FFMpegException( "mpeg4_decode_sprite_trajectory" );
}
if ( debug ) System.out.println( "Before qscale " + Integer.toHexString( in.showBits( 24 ) ) );
if ( shape != BIN_ONLY_SHAPE ) {
if ( debug ) System.out.println( "quant_precision " + quant_precision );
qscale = in.getBits( quant_precision );
int choma_qscale = qscale;
f_code = 1;
if ( pict_type != I_TYPE ) {
f_code = in.getBits(3);
}
b_code = 1;
if ( pict_type == B_TYPE ) {
b_code = in.getBits(3);
}
if ( debug ) System.out.println( "qp:" + qscale +
" fc:" + f_code + "," + b_code +
" " + (pict_type == I_TYPE ? "I" : (pict_type == P_TYPE ? "P" : (pict_type == B_TYPE ? "B" : "S"))) +
/* " size:" + in.availableBits() + */
" pro:" + (progressive_sequence?1:0) +
" alt:" + (alternate_scan?1:0) +
" top:" + (top_field_first?1:0) +
" " + (quarter_sample?"q":"h") + "pel" +
" part:" + (data_partitioning?1:0) +
" resync:" + (resync_marker?1:0) +
" w:" + num_sprite_warping_points +
" a:" + sprite_warping_accuracy +
" rnd:" + (no_rounding?0:1) +
" vot:" + vo_type + (vol_control_parameters?" VOLC" :" ") +
" dc:" + intra_dc_threshold );
if ( !scalability ) {
if ( shape != RECT_SHAPE && pict_type != I_TYPE ) {
in.getTrueFalse();
}
} else {
if ( enhancement_type ) {
boolean load_backward_shape = in.getTrueFalse();
if ( load_backward_shape ) throw new FFMpegException( "backward" );
}
in.getBits(2);
}
}
/* y_dc_scale_table */
return true;
}
public static final int VOL_HEADER = 0x120;
public static final int USER_DATA_STARTCODE = 0x1b2;
public static final int GOP_STARTCODE = 0x1b3;
public static final int VOP_STARTCODE = 0x1b6;
private void ff_mpeg4_decode_picture_header() throws FFMpegException {
/** Align */
if ( debug ) System.out.println( "ff_mpeg4_decode_picture_header" );
in.seek( in.getPos() - ( in.getPos() & 0x7 ) );
long startCode = 0xff;
while ( in.availableBits() >= 8 ) {
startCode = ((startCode << 8) | in.getBits(8)) & 0xffffffff;
if ( (int)(startCode & 0xffffff00) != 0x100 ) continue;
if ( debug ) System.out.println( "Start code " + Integer.toHexString((int)startCode & 0x1ff) );
switch( (int)(startCode & 0x1ff) ) {
case VOL_HEADER: {
decode_vol_header();
break;
}
case USER_DATA_STARTCODE: {
decode_user_data();
break;
}
case GOP_STARTCODE: {
mpeg4_decode_gop_header();
break;
}
case VOP_STARTCODE: {
if ( !decode_vop_header() ) {
break;
}
if ( debug ) System.out.println( "leaving decode_header" );
return;
}
default: {
break;
}
}
startCode = 0xff;
in.seek( in.getPos() - ( in.getPos() & 0x7 ) );
}
}
private int mb_x;
private int mb_y;
private int mb_stride;
/**
* Decode a frame
*/
protected void decodeFrame( Buffer out ) throws FFMpegException {
{
ff_mpeg4_decode_picture_header();
if ( pict_type == B_TYPE ) {
mb_type = mb_type_b_frame;
if ( debug2 ) DisplayOutput.debug.println( "Picture (B)" );
} else {
mb_type = mb_type_ip_frame;
if ( debug2 ) DisplayOutput.debug.println( "Picture (" + (pict_type==I_TYPE?"I":"P") + ")" );
}
mb_x = 0;
mb_y = 0;
do {
decode_slice();
} while ( mb_y < mbHeight );
/* if ( pict_type != B_TYPE ) {
displayOutput.endFrame();
displayOutput.showScreen(out);
} else {
displayOutput.endBFrame();
displayOutput.showScreen(out);
}
*/
displayOutput.expandIntoBoarder();
if ( pict_type != B_TYPE ) {
displayOutput.showNextScreen(out);
displayOutput.endIPFrame();
} else {
displayOutput.endBFrame();
displayOutput.showScreen(out);
}
}
}
/**
* Converts a byte array Buffer of DIVX video data
* into an integer array Buffer of video data.
*
* Note: The DIVX codec requires that the input buffer represents
* exactly one frame
*
* @return BUFFER_PROCESSED_OK The output buffer contains a valid frame
* @return BUFFER_PROCESSED_FAILED A decoding problem was encountered
*/
public int process( Buffer in, Buffer out ) {
try {
/* Extract the input data */
byte[] data = (byte[])in.getData();
this.in.addData( data, in.getOffset(), in.getLength() );
/* Decode and create an output image */
decodeFrame( out );
out.setTimeStamp( in.getTimeStamp() );
out.setFlags( in.getFlags() );
} catch ( Throwable e ) {
System.out.println( e );
e.printStackTrace();
return BUFFER_PROCESSED_OK;
}
return BUFFER_PROCESSED_OK;
}
/**
* Initialise the video codec for use.
*/
public void open() {
}
/**
* Deallocate resources, and shutdown.
*/
public void close() {
}
/**
* Reset the internal state of the video codec.
*/
public void reset() {
}
/**
* Retrives the name of this video codec: "DIVX video decoder"
* @return Codec name
*/
public String getName() {
return "DIVX video decoder";
}
/**
* This method returns the interfaces that can be used
* to control this codec. Currently no interfaces are defined.
*/
public Object[] getControls() {
return new Object[ 0 ];
}
/**
* This method returns an interface that can be used
* to control this codec. Currently no interfaces are defined.
*/
public Object getControl( String type ) {
return null;
}
/**
* Implement the Jffmpeg codec interface
*/
public boolean isCodecAvailable() {
return true;
}
/**
* Outofbands video size
*/
public void setVideoSize( Dimension size ) {
setInputFormat( new VideoFormat( "DIVX", size, -1, (new byte[ 0 ]).getClass(), 0 ) );
}
public void setEncoding( String encoding ) {
}
public void setIsRtp( boolean isRtp ) {
}
public void setIsTruncated( boolean isTruncated ) {
}
}