/*
* Java port of ffmpeg mpeg1/2 decoder.
* Copyright (c) 2003 Jonathan Hueber.
*
* Copyright (c) 2000,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.mpeg12;
import java.io.*;
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.mpeg12.data.Tables;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.data.MbPTypeVLC;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.data.MbBTypeVLC;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.data.AddressIncrementVlc;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.data.MotionVectorVlc;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.data.PatVLC;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.data.DiscreteCosineLuminanceVlc;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.data.DiscreteCosineChrominanceVlc;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.rltables.RLTable;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.rltables.Mpeg1RLTable;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.rltables.Mpeg2RLTable;
import net.sourceforge.jffmpeg.codecs.video.mpeg12.scantable.*;
import net.sourceforge.jffmpeg.codecs.utils.BitStream;
import net.sourceforge.jffmpeg.codecs.utils.VLCTable;
import net.sourceforge.jffmpeg.codecs.utils.FFMpegException;
import net.sourceforge.jffmpeg.codecs.video.mpeg.DisplayOutput;
/**
* This codec can decode MPEG 1 and MPEG 2 streams.
*/
public class MpegVideo implements Codec, JMFCodec {
/**
* Input and output variables
*/
protected BitStream in = new BitStream();
private DisplayOutput displayOutput;
/**
* Debugging tools
* - showInterlace shows interlaced streams side-by-side
* - skipBFrames does not display B frames (much faster)
* - debug dumps debugging data
*/
public static final boolean showInterlace = false;
public static final boolean skipBFrames = false;
public static final boolean debug = false;
/**
* Speed management
*/
private int numberOfFramesDelivered = 0;
public int targetFrameBuffer = 50;
public boolean hurryUp = false;
/**
* Synchronisation codes
*/
public static final int SYNC_BYTES = 0x000001;
public static final int SEQ_END_CODE = 0x00000b7;
public static final int SEQ_START_CODE = 0x00000b3;
public static final int GOP_START_CODE = 0x00000b8;
public static final int PICTURE_START_CODE = 0x0000000;
public static final int SLICE_MIN_START_CODE = 0x0000001;
public static final int SLICE_MAX_START_CODE = 0x00000af;
public static final int EXT_START_CODE = 0x00000b5;
public static final int USER_START_CODE = 0x00000b2;
/**
* Extension codes
*/
private static final int SEQUENCE_EXTENSION = 1;
private static final int SEQUENCE_DISPLAY_EXTENSION = 2;
private static final int QUANT_MATRIX_EXTENSION = 3;
private static final int PICTURE_DISPLAY_EXTENSION = 7;
private static final int PICTURE_CODING_EXTENSION = 8;
/**
* Picture types
*/
public static final int I_TYPE = 1;
public static final int P_TYPE = 2;
public static final int B_TYPE = 3;
public static final int SKIP_FRAME_TYPE = -1;
public static final int PICT_FRAME = 3;
public static final int MT_FIELD = 1;
public static final int MT_FRAME = 2;
public static final int MT_16X8 = 2;
public static final int MT_DMV = 3;
/**
* Macroblock motion types
*/
public static final int MV_TYPE_16X16 = 0; // 1 vector for the whole mb
public static final int MV_TYPE_8X8 = 1; // 4 vectors (h263, 4MV)
public static final int MV_TYPE_16X8 = 2; // 2 vectors, one per 16x8 block
public static final int MV_TYPE_FIELD = 3; // 2 vectors, one per field
public static final int MV_TYPE_DMV = 4; // 2 vectors, special mpeg2 Dual Prime Vectors
/**
* Macroblock motion direction (from last or to next I/P frame)
*/
public static final int MV_DIR_FORWARD = 2;
public static final int MV_DIR_BACKWARD = 1;
private int currentHeader;
/**
* Width/Height and format (mpeg2 or mpeg1)
*/
private int mbWidth;
private int mbHeight;
protected boolean mpeg2;
/**
* Sequence header
*/
protected int width;
protected int height;
protected int aspectRatio;
protected int frame_rate_index;
protected int bit_rate;
protected float frameRate = 299/10;
/**
* Internal data tables
*/
private ScanTable alternateVerticalScanTable = new AlternateVerticalScan();
private ScanTable alternateHorizontalScanTable = new AlternateHorizontalScan();
private ScanTable zigZagDirect = new ZigZagDirect();
private ScanTable intraScanTable = new ZigZagDirect();
private ScanTable interScanTable = new ZigZagDirect();
private ScanTable intraHScanTable = new AlternateHorizontalScan();
private ScanTable intraVScanTable = new AlternateVerticalScan();
private final int[] dsp_idct_permutation = Tables.getDspIdctPermutation();
private final int[] ff_mpeg1_default_intra_matrix = Tables.getMpeg1DefaultIntraMatrix();
private final int[] ff_mpeg1_default_non_intra_matrix = Tables.getMpeg1DefaultNonIntraMatrix();
private final int[] non_linear_qscale = Tables.getNonLinearQscale();
public final VLCTable mbincr_vlc = new AddressIncrementVlc();
public final VLCTable mv_vlc = new MotionVectorVlc();
public final RLTable rl_mpeg1 = new Mpeg1RLTable();
public final RLTable rl_mpeg2 = new Mpeg2RLTable();
private final VLCTable dc_lum_vlc = new DiscreteCosineLuminanceVlc();
private final VLCTable dc_chroma_vlc = new DiscreteCosineChrominanceVlc();
private int[] ptype2mb_type = Tables.getPType2mb_type();
private int[] btype2mb_type = Tables.getBType2mb_type();
private VLCTable mb_ptype_vlc = new MbPTypeVLC();
private VLCTable mb_btype_vlc = new MbBTypeVLC();
private VLCTable mb_pat_vlc = new PatVLC();
/**
* Internal State - Quantization matricies
*/
private int[] intra_matrix = new int[ 64 ];
private int[] inter_matrix = new int[ 64 ];
private int[] chroma_intra_matrix = new int[ 64 ];
private int[] chroma_inter_matrix = new int[ 64 ];
/**
* Internal State - Motion code
*/
private int[][] motion_val;
private int mv_dir;
private int mv_type;
private int motion_type;
private int[] mv = new int[ 8 ];
private int[] last_mv = new int[ 8 ];
private boolean[] full_pel = new boolean[ 2 ];
private boolean[] field_select = new boolean[ 4 ];
/**
* Internal State - I type macroblock
*/
private boolean mb_intra = true;
/**
* Pels forming a Macroblock
*/
protected int mb_type;
public 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 ];
/**
* Cache for pel DC compoent decoding
*/
private int[] last_dc = new int[] { 0x080, 0x080, 0x080 };
/**
* Sequence extension information
*/
protected int profile;
protected int level;
protected boolean progressive_sequence;
protected int vdv_buf_ext;
/**
* Pan and scan information
*/
private int panScanWidth;
private int panScanHeight;
protected int intra_dc_precision;
protected int picture_structure = PICT_FRAME;
protected boolean top_field_first;
protected boolean frame_pred_frame_dct;
protected boolean concealment_motion_vectors;
protected boolean q_scale_type;
protected boolean intra_vlc_format;
protected boolean alternate_scan;
protected boolean repeat_first_field;
protected boolean chroma_420_type;
protected boolean progressive_frame;
protected boolean first_field;
/**
* Picture definistion codes
*/
protected int pict_type;
protected int picture_number;
protected int[] mpeg_f_code = new int[ 4 ];
protected int y_dc_scale;
protected int c_dc_scale;
protected boolean first_slice;
/**
* Current macroblock positions
*/
protected boolean field_pic;
protected int resync_mb_x;
protected int resync_mb_y;
protected int mb_x;
protected int mb_y;
/**
* scaling
*/
protected boolean interlaced_dct;
protected int repeat_pict;
protected int qscale;
protected int mb_skip_run;
protected final float[] frameRateTable = Tables.getFrameRateTable();
/**
* Creates a new instance of MpegVideo
* The only useful initialisation for this codec is
* the video widtha and height.
*/
public MpegVideo() {
}
/**
* Initialise the codec for this width and height
*/
private void initialise( int width, int height ) {
/**
* Calculate number of macroblocks
*/
mbWidth = (width + 15) / 16;
mbHeight = (height + 15) / 16;
if ( displayOutput == null ) displayOutput = new DisplayOutput(mbWidth, mbHeight);
/**
* 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 arrays
*/
motion_val = new int[ 1 + (mbWidth * 2 + 2) * mbHeight * 2 * 4 ][2];
}
/**
* This packet describes the highest level video attributes:
* - Width, Height
* - Aspect ratio
* - Frame/Bit rate
* - Luminance and Chrominance matricies
*/
private void mpeg1_decode_sequence() {
width = in.getBits( 12 );
height = in.getBits( 12 );
aspectRatio = in.getBits( 4 );
frame_rate_index = in.getBits( 4 );
frameRate = frameRateTable[ frame_rate_index ];
// System.out.println( "fri " + frameRate );
bit_rate = in.getBits( 18 ) * 400;
in.getTrueFalse();
in.getBits(10);
in.getTrueFalse();
/* Get Intra Matrix */
if ( in.getTrueFalse() ) {
for ( int i = 0; i < 64; i++ ) {
int v = in.getBits( 8 );
int j = intraScanTable.getPermutated()[ i ];
intra_matrix[ j ] = v;
chroma_intra_matrix[ j ] = v;
}
} else {
for ( int i = 0; i < 64; i++ ) {
int j = dsp_idct_permutation[ i ];
int v = ff_mpeg1_default_intra_matrix[ i ];
intra_matrix[ j ] = v;
chroma_intra_matrix[ j ] = v;
}
}
/* Get Non-intra Matrix */
if ( in.getTrueFalse() ) {
for ( int i = 0; i < 64; i++ ) {
int v = in.getBits( 8 );
int j = intraScanTable.getPermutated()[ i ];
inter_matrix[ j ] = v;
chroma_inter_matrix[ j ] = v;
}
} else {
for ( int i = 0; i < 64; i++ ) {
int j = dsp_idct_permutation[ i ];
int v = ff_mpeg1_default_non_intra_matrix[ i ];
inter_matrix[ j ] = v;
chroma_inter_matrix[ j ] = v;
}
}
/* Initialise variables for Mpeg1 */
progressive_sequence = true;
progressive_frame = true;
picture_structure = PICT_FRAME;
frame_pred_frame_dct = true;
}
/**
* This packet is only found in MPEG2 streams. It defines:
* - Extended Width and height
*/
private void mpeg_decode_sequence_extension() {
in.getTrueFalse(); // Profile and level escape
profile = in.getBits(3);
level = in.getBits(4);
progressive_sequence = in.getTrueFalse();
in.getBits( 2 ); //Chroma format
width |= (in.getBits( 2 ) << 12);
height |= (in.getBits( 2 ) << 12);
bit_rate = ((bit_rate/400)|(in.getBits(12) << 12)) * 400;
in.getTrueFalse();
vdv_buf_ext = in.getBits( 8 );
in.getTrueFalse();
int frame_rate_ext_n = in.getBits(2);
int frame_rate_ext_d = in.getBits(5);
// System.out.println( "Frame rate " + frame_rate_ext_n + " " + frame_rate_ext_d );
// System.out.println( "progressive " + progressive_sequence );
mpeg2 = true;
}
/**
* This packet is used for pan and scan. It describes:
* - Width and Height of pan area
*/
private void mpeg_decode_sequence_display_extension() {
in.getBits(3);
if (in.getTrueFalse()) {
in.getBits(24);
}
int width = in.getBits( 14 );
in.getTrueFalse();
int height = in.getBits( 14 );
in.getTrueFalse();
panScanWidth = 16 * width;
panScanHeight = 16 * height;
}
/**
* This packet describes a variety of display variables:
* - Field selection
*
*/
private void mpeg_decode_picture_coding_extension() {
full_pel[0] = false;
full_pel[1] = false;
mpeg_f_code[0] = in.getBits(4);
mpeg_f_code[1] = in.getBits(4);
mpeg_f_code[2] = in.getBits(4);
mpeg_f_code[3] = in.getBits(4);
intra_dc_precision = in.getBits(2);
picture_structure = in.getBits(2);
top_field_first = in.getTrueFalse();
frame_pred_frame_dct = in.getTrueFalse();
concealment_motion_vectors = in.getTrueFalse();
q_scale_type = in.getTrueFalse();
intra_vlc_format = in.getTrueFalse();
alternate_scan = in.getTrueFalse();
repeat_first_field = in.getTrueFalse();
chroma_420_type = in.getTrueFalse();
progressive_frame = in.getTrueFalse();
if( picture_structure == PICT_FRAME ) {
first_field = false;
} else {
first_field = !first_field;
/** Removed memset0 */
}
if( alternate_scan ) {
intraScanTable = alternateVerticalScanTable;
interScanTable = alternateVerticalScanTable;
intraHScanTable = alternateVerticalScanTable;
intraVScanTable = alternateVerticalScanTable;
} else {
intraScanTable = zigZagDirect;
interScanTable = zigZagDirect;
intraHScanTable = alternateHorizontalScanTable;
intraVScanTable = alternateVerticalScanTable;
}
/* composite display not parsed */
if ( debug )
{
System.out.println("intra_dc_precision=" + intra_dc_precision);
System.out.println("picture_structure=" + picture_structure);
System.out.println("top field first=" + (top_field_first?1:0));
System.out.println("repeat first field=" + (repeat_first_field?1:0));
System.out.println("conceal=" + (concealment_motion_vectors?1:0));
System.out.println("intra_vlc_format=" + (intra_vlc_format?1:0));
System.out.println("alternate_scan=" + (alternate_scan?1:0));
System.out.println("frame_pred_frame_dct=" + (frame_pred_frame_dct?1:0));
System.out.println("progressive_frame=" +( progressive_frame?1:0));
}
}
/**
* Extract quantisation matrix extension
*/
private void mpeg_decode_quant_matrix_extension() {
int i, v, j;
if (in.getTrueFalse()) {
for(i=0;i<64;i++) {
v = in.getBits(8);
j= zigZagDirect.getPermutated()[i];
intra_matrix[j] = v;
chroma_intra_matrix[j] = v;
}
}
if (in.getTrueFalse()) {
for(i=0;i<64;i++) {
v = in.getBits(8);
j= zigZagDirect.getPermutated()[i];
inter_matrix[j] = v;
chroma_inter_matrix[j] = v;
}
}
if (in.getTrueFalse()) {
for(i=0;i<64;i++) {
v = in.getBits(8);
j= zigZagDirect.getPermutated()[i];
chroma_intra_matrix[j] = v;
}
}
if (in.getTrueFalse()) {
for(i=0;i<64;i++) {
v = in.getBits(8);
j= zigZagDirect.getPermutated()[i];
chroma_inter_matrix[j] = v;
}
}
}
/**
* Decode extension codes
*/
private void mpeg_decode_extension() {
int extensionType = in.getBits( 4 );
switch (extensionType) {
case SEQUENCE_EXTENSION: {
mpeg_decode_sequence_extension();
break;
}
case SEQUENCE_DISPLAY_EXTENSION: {
mpeg_decode_sequence_display_extension();
break;
}
case QUANT_MATRIX_EXTENSION: {
mpeg_decode_quant_matrix_extension();
break;
}
case PICTURE_DISPLAY_EXTENSION: {
break;
}
case PICTURE_CODING_EXTENSION: {
mpeg_decode_picture_coding_extension();
break;
}
default: {
break;
}
}
}
/**
* Start to decode a frame. This includes:
* - Frame reference number
* - Frame type (I, P, or B)
* - Field management
*/
private void mpeg1_decode_picture() {
int frameReference = in.getBits( 10 );
int f_code;
pict_type = in.getBits( 3 );
// System.out.println( "pict_type=" + pict_type + " number=" + reference + " " + frame_rate_index + " " + inverseTelecine );
in.getBits(16);
if ( pict_type == P_TYPE || pict_type == B_TYPE ) {
full_pel[0] = in.getTrueFalse();
f_code = in.getBits(3);
mpeg_f_code[0] = f_code;
mpeg_f_code[1] = f_code;
}
if ( pict_type == B_TYPE ) {
full_pel[1] = in.getTrueFalse();
f_code = in.getBits(3);
mpeg_f_code[2] = f_code;
mpeg_f_code[3] = f_code;
}
y_dc_scale = 8;
c_dc_scale = 8;
first_slice = true;
}
/**
* Decode a motion vector
*/
private int mpeg_decode_motion( int fcode, int predicted ) throws FFMpegException {
int code = in.getVLC( mv_vlc );
if ( code == 0 ) return predicted;
boolean sign = in.getTrueFalse();
int shift = fcode - 1;
if ( shift != 0 ) {
code = (code - 1) << shift;
code |= in.getBits( shift );
code++;
}
if ( sign ) code = -code;
code += predicted;
int l = 1 << (shift + 4);
code = ((code + l)&(l*2 - 1)) - l;
return code;
}
/**
* Decode a DC value
*/
private int decode_dc( int component ) throws FFMpegException {
int code = in.getVLC( (component == 0) ? dc_lum_vlc : dc_chroma_vlc );
if ( code == 0 ) {
return 0;
} else {
int diff = in.getBits( code );
if ((diff & (1 << (code - 1))) == 0) {
diff = (-1 << code) | (diff + 1);
}
return diff;
}
}
/* MPEG 1 **************************************************************************/
/**
* Decode a pel for a predicted MPEG1 macroblock
*/
private void mpeg1_decode_block_inter( int[] block, int blockNumber ) throws FFMpegException {
RLTable rltable = rl_mpeg1;
ScanTable scantable = intraScanTable;
int[] quant_matrix= inter_matrix;
{
int i = -1;
int j;
/* special case for the first coef. no need to add a second vlc table */
int v = in.showBits( 2 );
if ((v & 2) != 0) {
in.getBits(2);
level = ( 3 * qscale * quant_matrix[0] ) >> 4;
level = (level-1)|1;
if( ( v & 1 ) != 0) {
level= - level;
}
block[0] = level;
i++;
}
/* now quantify & encode AC coefs */
for(;;) {
int index = in.getVLC( rltable );
int level = rltable.getLevel( index );
int run = rltable.getRun( index );
if ( level == 127 ) {
break;
} else if( level != 0 ) {
i += run;
j = scantable.getPermutated()[ i ];
level= (( level * 2 + 1) * qscale * quant_matrix[j] ) >> 4;
level= ( level - 1 )|1;
if ( in.getTrueFalse() ) {
level = -level;
}
} else {
/* escape */
run = in.getBits(6)+1;
level = in.getBits(8);
if ( (level & (1 << 7)) != 0) level |= ~255;
if (level == -128) {
level = in.getBits(8) - 256;
} else if (level == 0) {
level = in.getBits(8);
}
i += run;
j = scantable.getPermutated()[i];
if ( level<0 ) {
level= -level;
level= ((level*2+1)*qscale*quant_matrix[j])>>4;
level= (level-1)|1;
level= -level;
} else {
level= ((level*2+1)*qscale*quant_matrix[j])>>4;
level= (level-1)|1;
}
}
if (i > 63 ) {
throw new MpegException( "Illegal MB code" );
}
block[j] = level;
}
}
}
/**
* Decode a pel for an I-type MPEG1 macroblock
*/
private void mpeg1_decode_block_intra( int[] block, int blockNumber ) throws FFMpegException {
RLTable rltable = rl_mpeg1;
ScanTable scantable = intraScanTable;
int[] quant_matrix= intra_matrix;
/* DC coef */
int component = (blockNumber <= 3 ? 0 : blockNumber - 4 + 1);
int diff = decode_dc( component);
int dc = last_dc[component];
dc += diff;
last_dc[component] = dc;
block[0] = dc << 3;
int i = 0;
int j;
{
/* now quantify & encode AC coefs */
for(;;) {
int index = in.getVLC( rltable );
int level = rltable.getLevel( index );
int run = rltable.getRun( index );
if( level == 127 ) {
break;
} else if( level != 0 ) {
i += run;
j = scantable.getPermutated()[ i ];
level= ( level * qscale * quant_matrix[j]) >> 3;
level= ( level - 1 )|1;
if ( in.getTrueFalse() ) {
level = -level;
}
} else {
/* escape */
run = in.getBits(6) + 1;
level = in.getBits(8);
if ( (level & (1<<7)) != 0 ) level |= ~255;
if ( level == -128 ) {
level = in.getBits(8) - 256;
} else if ( level == 0 ) {
level = in.getBits(8);
}
i += run;
j = scantable.getPermutated()[i];
if( level < 0 ) {
level= -level;
level= ( level * qscale * quant_matrix[j] ) >> 3;
level= ( level - 1 ) | 1;
level= -level;
}else{
level= ( level * qscale * quant_matrix[j] ) >> 3;
level= ( level - 1 ) | 1;
}
}
if (i > 63){
throw new MpegException( "Error decoding mb" );
}
block[j] = level;
}
}
}
/* MPEG 2 **************************************************************************/
/**
* Decode a pel for a predicted MPEG2 macroblock
*/
private void mpeg2_decode_block_intra(int[] block, int blockNumber) throws FFMpegException {
int[] intra_scantable = intraScanTable.getPermutated();
int[] quant_matrix = (blockNumber < 4) ? intra_matrix : chroma_intra_matrix;
int component = (blockNumber < 4) ? 0 : blockNumber - 3;
last_dc[ component ] += decode_dc( component );
block[0] = last_dc[ component ] << (3 - intra_dc_precision);
int mismatch = block[0]^1;
RLTable rltable = intra_vlc_format ? rl_mpeg2 : rl_mpeg1;
int i = 0;
int j = 0;
for (;;) {
int index = in.getVLC( rltable );
int level = rltable.getLevel( index );
int run = rltable.getRun( index );
if ( level == 127 ) break;
if ( level != 0 ) {
i += run;
j = intra_scantable[ i ];
level = (level * qscale * quant_matrix[j]) >> 4;
if ( in.getTrueFalse() ) {
level = (level ^ ~0) + 1;
}
} else {
/* escape code */
run = in.getBits(6) + 1;
if ( in.getTrueFalse() ) {
level = in.getBits(11) | (~0x7ff);
} else {
level = in.getBits(11);
}
i += run;
j = intra_scantable[ i ];
if ( level < 0 ) {
level = ((-level) * qscale * quant_matrix[j]) >> 4;
level = -level;
} else {
level = (level * qscale * quant_matrix[j]) >> 4;
}
}
if ( i > 63 ) throw new MpegException( "Error" );
mismatch ^= level;
block[j] = level;
}
block[63] ^= mismatch & 1;
}
/**
* Decode a pel for an I-TYPE MPEG2 macroblock
*/
private void mpeg2_decode_block_non_intra(int[] block, int blockNumber) throws FFMpegException {
int[] intra_scantable = intraScanTable.getPermutated();
int[] quant_matrix = (blockNumber < 4) ? inter_matrix : chroma_inter_matrix;
int mismatch = 1;
int i = -1;
int v = in.showBits(2);
if ( (v & 2) == 2 ) {
v = in.getBits(2);
block[ 0 ] = (3 * qscale * quant_matrix[ 0 ]) >> 5;
if ( (v & 1) == 1 ) block[0] = -block[0];
mismatch ^= block[0];
i++;
}
RLTable rltable = rl_mpeg1;
int j = 0;
for (;;) {
int index = in.getVLC( rltable );
int level = rltable.getLevel( index );
int run = rltable.getRun( index );
if ( level == 127 ) break;
if ( level != 0 ) {
i += run;
j = intra_scantable[ i ];
level = ((level*2+1) * qscale * quant_matrix[j]) >> 5;
if ( in.getTrueFalse() ) {
level = (level ^ ~0) + 1;
}
} else {
/* escape code */
run = in.getBits(6) + 1;
if ( in.getTrueFalse() ) {
level = in.getBits(11) | (~0x7ff);
} else {
level = in.getBits(11);
}
i += run;
j = intra_scantable[ i ];
if ( level < 0 ) {
level = ((-level*2+1) * qscale * quant_matrix[j]) >> 5;
level = -level;
} else {
level = ((level*2+1) * qscale * quant_matrix[j]) >> 5;
}
}
if ( i > 63 ) throw new MpegException( "Error" );
mismatch ^= level;
block[j] = level;
}
block[63] ^= mismatch & 1;
}
/**
* Decode a macroblock and the motion vector
*/
private void mpeg_decode_mb() throws FFMpegException {
//System.out.println( "decode_mb: x=" + mb_x + " y=" + mb_y);
if ( mb_skip_run-- != 0 ) {
if ( pict_type == I_TYPE ) throw new Error( "skip in IFrame" );
/* skip mb */
mv_type = MV_TYPE_16X16;
if (pict_type == P_TYPE) {
/* if P type, zero motion vector is implied */
mv_dir = MV_DIR_FORWARD;
mv[ 0 ] = 0;
mv[ 1 ] = 0;
last_mv[ 0 ] = 0;
last_mv[ 1 ] = 0;
last_mv[ 2 ] = 0;
last_mv[ 3 ] = 0;
} else {
/* if B type, reuse previous vectors and directions */
mv[ 0 ] = last_mv[ 0 ];
mv[ 1 ] = last_mv[ 1 ];
mv[ 4 ] = last_mv[ 4 ];
mv[ 5 ] = last_mv[ 5 ];
}
mb_intra = false;
//TODO motion code
return;
}
switch( pict_type ) {
case I_TYPE: {
if ( in.getTrueFalse() ) {
mb_type = Tables.MB_TYPE_INTRA;
} else if ( in.getTrueFalse() ) {
mb_type = Tables.MB_TYPE_INTRA | Tables.MB_TYPE_QUANT;
} else {
throw new MpegException( "Invalid mb type" );
}
break;
}
case P_TYPE: {
mb_type = ptype2mb_type[ in.getVLC( mb_ptype_vlc ) ];
break;
}
case B_TYPE: {
mb_type = btype2mb_type[ in.getVLC( mb_btype_vlc ) ];
break;
}
}
if ( (Tables.MB_IS_INTRA_MASK & mb_type) != 0 ) {
/**
* This is an I-type macro block
*/
if ( picture_structure == PICT_FRAME && !frame_pred_frame_dct ) {
interlaced_dct = in.getTrueFalse();
}
if ( (Tables.MB_TYPE_QUANT & mb_type) != 0 ) {
qscale = get_qscale();
}
if ( concealment_motion_vectors ) {
if ( picture_structure != PICT_FRAME ) in.getTrueFalse();
mv[ 0 ] = mpeg_decode_motion(mpeg_f_code[1], last_mv[0]);
mv[ 1 ] = mpeg_decode_motion(mpeg_f_code[1], last_mv[1]);
last_mv[0] = mv[0];
last_mv[2] = mv[0];
last_mv[1] = mv[1];
last_mv[3] = mv[1];
in.getTrueFalse();
} else {
last_mv[0] = 0;
last_mv[2] = 0;
last_mv[1] = 0;
last_mv[3] = 0;
last_mv[4] = 0;
last_mv[6] = 0;
last_mv[5] = 0;
last_mv[7] = 0;
}
mb_intra = true;
if (mpeg2) {
for (int i = 0; i < NUMBER_OF_BLOCKS; i++ ) {
mpeg2_decode_block_intra(block[i], i);
}
} else {
for( int i = 0; i < 6; i++ ) {
mpeg1_decode_block_intra( block[i], i );
}
}
} else {
/**
* This is a P type macro block
*/
if ( (mb_type & Tables.MB_TYPE_ZERO_MV) != 0 ) {
if ( picture_structure == PICT_FRAME && !frame_pred_frame_dct ) {
interlaced_dct = in.getTrueFalse();
}
if ( (Tables.MB_TYPE_QUANT & mb_type) != 0 ) {
qscale = get_qscale();
}
mv_dir = MV_DIR_FORWARD;
mv_type = MV_TYPE_16X16;
last_mv[0] = 0;
last_mv[1] = 0;
last_mv[2] = 0;
last_mv[3] = 0;
mv[0] = 0;
mv[1] = 0;
} else {
if ( frame_pred_frame_dct ) {
motion_type = MT_FRAME;
} else {
motion_type = in.getBits(2);
}
if ( picture_structure == PICT_FRAME && !frame_pred_frame_dct
&& ((mb_type & Tables.MB_TYPE_PAT) != 0)) {
interlaced_dct = in.getTrueFalse();
}
if ( (Tables.MB_TYPE_QUANT & mb_type) != 0 ) {
qscale = get_qscale();
}
/**
* Decode motion vectors for macro block
*/
mv_dir = 0;
for ( int i = 0; i < 2; i++ ) {
if ( 0 == (mb_type & ((Tables.MB_TYPE_P0L0|Tables.MB_TYPE_P1L0) << (2 * i))) ) {
continue;
}
mv_dir |= (MV_DIR_FORWARD >> i);
switch ( motion_type ) {
case MT_FRAME: {
if ( picture_structure == PICT_FRAME ) {
/* MT_FRAME */
// System.out.println( "MT_FRAME" );
mb_type |= Tables.MB_TYPE_16x16;
mv_type = MV_TYPE_16X16;
mv[i * 4] = mpeg_decode_motion( mpeg_f_code[i* 2],
last_mv[i * 4] );
last_mv[i * 4] = mv[i * 4];
last_mv[i * 4 + 2] = mv[i * 4];
mv[i * 4 + 1] = mpeg_decode_motion( mpeg_f_code[i * 2 + 1],
last_mv[i * 4 + 1] );
last_mv[i * 4 + 1] = mv[i * 4 + 1];
last_mv[i * 4 + 3] = mv[i * 4 + 1];
if ( full_pel[i] ) {
mv[i * 4] <<= 1;
mv[i * 4 + 1] <<= 1;
}
} else {
/* MT_16X8 */
// System.out.println( "MT_16X8" );
mb_type |= Tables.MB_TYPE_16x8;
mv_type = MV_TYPE_16X8;
for ( int j = 0; j < 2; j++ ) {
field_select[i * 2 + j] = in.getTrueFalse();
for ( int k = 0; k < 2; k++ ) {
mv[i * 4 + j * 2 + k] = mpeg_decode_motion( mpeg_f_code[i * 2 + k],
last_mv[i * 4 + j * 2 + k] );
last_mv[i * 4 + j * 2 + k] = mv[i * 4 + j * 2 + k];
}
}
}
break;
}
case MT_FIELD: {
// System.out.println( "MT_FIELD" );
mv_type = MV_TYPE_FIELD;
if ( picture_structure == PICT_FRAME ) {
mb_type |= Tables.MB_TYPE_16x8 | Tables.MB_TYPE_INTERLACED;
for ( int j = 0; j < 2; j++ ) {
field_select[i * 2 + j] = in.getTrueFalse();
last_mv[i * 4 + j * 2] = mpeg_decode_motion( mpeg_f_code[i * 2],
last_mv[i * 4 + j * 2] );
mv[i * 4 + j * 2] = last_mv[i * 4 + j * 2];
last_mv[i * 4 + j * 2 + 1] = mpeg_decode_motion( mpeg_f_code[i * 2 + 1],
last_mv[i * 4 + j * 2 + 1] >> 1 );
mv[i * 4 + j * 2 + 1] = last_mv[i * 4 + j * 2 + 1];
last_mv[i * 4 + j * 2 + 1] <<= 1;
}
} else {
mb_type |= Tables.MB_TYPE_16x16;
field_select[i * 2] = in.getTrueFalse();
for ( int k = 0; k < 2; k++ ) {
last_mv[i * 4 + k] = mpeg_decode_motion( mpeg_f_code[i * 2 + k],
last_mv[i * 4 + k] );
last_mv[i * 4 + 2 + k] = last_mv[i * 4 + k];
mv[i * 4 + k] = last_mv[i * 4 + k];
}
}
break;
}
case MT_DMV: {
System.out.println( "MT_DMV" );
break;
}
default: {
System.out.println( "UNKNOWN" );
break;
}
}
}
}
/**
* Decode intra macro block
*/
mb_intra = false;
if ( (mb_type & Tables.MB_TYPE_PAT) != 0 ) {
int cbp = in.getVLC( mb_pat_vlc );
cbp++;
if (mpeg2) {
for (int i = 0; i < NUMBER_OF_BLOCKS; i++ ) {
if ( (cbp & 32) == 32 ) {
mpeg2_decode_block_non_intra(block[i], i);
} else {
}
cbp *= 2;
}
} else {
for( int i = 0; i < 6; i++ ) {
if ( (cbp & 32) == 32 ) {
mpeg1_decode_block_inter( block[i], i);
} else {
}
cbp *= 2;
}
}
}
}
}
/**
* Read the quantization scale
*/
private int get_qscale() {
int qscale;
if (mpeg2) {
if (q_scale_type) {
qscale = non_linear_qscale[in.getBits(5)];
} else {
qscale = in.getBits(5) << 1;
}
} else {
/* for mpeg1, we use the generic unquant code */
qscale = in.getBits(5);
}
return qscale;
}
private void MPV_decode_mb() {
/**
* If this is not an I type macroblock the dc cache
* needs to be reset
*/
if (!mb_intra) {
last_dc[0] = 128 << intra_dc_precision;
last_dc[1] = 128 << intra_dc_precision;
last_dc[2] = 128 << intra_dc_precision;
}
/**
* If we are skipping B frames go no further than this
*/
if ( (skipBFrames || hurryUp) && pict_type == B_TYPE ) return;
if ( !mb_intra ) {
/**
* P or B Type macroblock
*/
// if ( pict_type == I_TYPE ) throw new Error( "non intra I frame" );
int x = mb_x;
int y = mb_y;
/**
* Forward motion
*/
boolean average = false;
if ( (mv_dir & MV_DIR_FORWARD) != 0 ) {
if ( pict_type == P_TYPE ) {
displayOutput.moveFromNext( x, y, mv, 0, false, average, mv_type, field_select, 0 );
} else {
displayOutput.moveFromLast( x, y, mv, 0, true, average, mv_type, field_select, 0 );
}
average = true;
}
/**
* Backward motion
*/
if ( (mv_dir & MV_DIR_BACKWARD) != 0 ) {
if ( pict_type == P_TYPE ) {
// throw new Error( "Backwards motion in P Frame" );
} else {
displayOutput.moveFromNext( x, y, mv, 4, true, average, mv_type, field_select, 2 );
}
}
/**
* Add delta corrections
*/
displayOutput.addLuminanceIdct( x * 2, y * 2, block[ 0 ], interlaced_dct );
displayOutput.addLuminanceIdct( x * 2 + 1, y * 2, block[ 1 ], interlaced_dct );
displayOutput.addLuminanceIdct( x * 2, y * 2 + 1, block[ 2 ], interlaced_dct );
displayOutput.addLuminanceIdct( x * 2 + 1, y * 2 + 1, block[ 3 ], interlaced_dct );
displayOutput.addRedIdct( x, y, block[ 5 ] );
displayOutput.addBlueIdct( x, y, block[ 4 ] );
} else {
int x = mb_x;
int y = mb_y;
/**
* I Type macroblock - just display it
*/
displayOutput.putLuminanceIdct( x * 2, y * 2, block[ 0 ], interlaced_dct );
displayOutput.putLuminanceIdct( x * 2 + 1, y * 2, block[ 1 ], interlaced_dct );
displayOutput.putLuminanceIdct( x * 2, y * 2 + 1, block[ 2 ], interlaced_dct );
displayOutput.putLuminanceIdct( x * 2 + 1, y * 2 + 1, block[ 3 ], interlaced_dct );
displayOutput.putRedIdct( x, y, block[ 5 ] );
displayOutput.putBlueIdct( x, y, block[ 4 ] );
}
}
/**
* Decode a slice (row of macroblocks)
*/
private void mpeg_decode_slice(int sliceNumber ) throws FFMpegException {
last_dc[0]=1 << (7 + intra_dc_precision);
last_dc[1]=last_dc[0];
last_dc[2]=last_dc[0];
last_mv[0] = 0;
last_mv[1] = 0;
last_mv[2] = 0;
last_mv[3] = 0;
last_mv[4] = 0;
last_mv[5] = 0;
last_mv[6] = 0;
last_mv[7] = 0;
field_pic = (picture_structure != PICT_FRAME);
//ff_mpeg1_clean_buffers;
interlaced_dct = false;
if ( first_slice ) {
if ( first_field || !field_pic ) {
repeat_pict = 0;
if ( repeat_first_field ) {
if ( progressive_sequence ) {
repeat_pict = top_field_first ?4:2;
} else if ( progressive_frame ) {
repeat_pict = 1;
}
}
} else {
for ( int i = 0; i < 4; i++ ) {
/* TODO picture_data */
}
}
}
first_slice = false;
/*
System.out.println( "Slice: " + sliceNumber );
System.out.println( "qp:" + qscale +
" fc:" + mpeg_f_code[0][0] + " " + mpeg_f_code[0][1] + " " + mpeg_f_code[1][0] + " " + mpeg_f_code[1][1] +
" " + (pict_type == I_TYPE ? "I" : (pict_type == P_TYPE ? "P" : (pict_type == B_TYPE ? "B" : "S"))) +
" " + (progressive_sequence ? "pro" :"") +
" " + (alternate_scan ? "alt" :"") +
" " + (top_field_first ? "top" :"") +
" dc:" + intra_dc_precision +
" pstruct:" + picture_structure +
" fdct:" + frame_pred_frame_dct +
" cmv:" + concealment_motion_vectors +
" qtype:" + q_scale_type +
" ivlc:" + intra_vlc_format +
" rff:" + repeat_first_field + " " + (chroma_420_type ? "420" :""));
*/
qscale = get_qscale();
/* Extra slice information */
while ( in.getTrueFalse() ) {
in.getBits( 8 );
}
mb_x = 0;
int code;
do {
code = in.getVLC( mbincr_vlc );
if ( code <= 33 ) mb_x += code;
} while ( code >= 33 );
resync_mb_x = mb_x;
resync_mb_y = sliceNumber;
mb_y =sliceNumber;
/**
* Initialise the quick lookup arrays
* ff_init_block_index( mb_x, mb_y );
*/
blockIndex[ 0 ] = blockWrap[ 0 ] * ( mb_y * 2 + 1 ) - 1 + mb_x * 2;
blockIndex[ 1 ] = blockIndex[ 0 ] + 1;
blockIndex[ 2 ] = blockIndex[ 0 ] + blockWrap[ 0 ];
blockIndex[ 3 ] = blockIndex[ 2 ] + 1;
blockIndex[ 4 ] = blockWrap[ 0 ] * ( mbHeight * 2 + 2 )
+ blockWrap[ 4 ] * ( mb_y + 1 ) + mb_x;
blockIndex[ 5 ] = blockIndex[ 4 ] + blockWrap[ 4 ] * (mbHeight + 2);
mb_skip_run = 0;
boolean endOfSlice = false;
while (!endOfSlice && ((mb_y << (field_pic?1:0)) < mbHeight)) {
if ( mb_x > mbWidth ) System.out.println( "X too large" );
//clear_blocks
for ( int i = 0; i < NUMBER_OF_BLOCKS; i++ ) {
for ( int j = 0; j < 64; j++ ) block[i][j] = 0;
}
mpeg_decode_mb();
if ( pict_type != B_TYPE ) { //Note this is probably unnecessary
int wrap = blockWrap[ 0 ];
int xy = mb_x * 2 + 1 + ( mb_y * 2 + 1 ) * wrap;
int motion_x;
int motion_y;
if ( mb_intra ) {
motion_x = 0;
motion_y = 0;
} else if ( mv_type == MV_TYPE_16X16 ) {
motion_x = mv[0];
motion_y = mv[1];
} else {
/* Field */
motion_x = mv[0] + mv[2];
motion_y = mv[1] + mv[3];
motion_x = (motion_x >> 1) | (motion_x & 1);
}
motion_val[ xy ][ 0 ] = motion_x;
motion_val[ xy ][ 1 ] = motion_y;
motion_val[ xy + 1 ][ 0 ] = motion_x;
motion_val[ xy + 1 ][ 1 ] = motion_y;
motion_val[ xy + wrap ][ 0 ] = motion_x;
motion_val[ xy + wrap ][ 1 ] = motion_y;
motion_val[ xy + wrap + 1 ][ 0 ] = motion_x;
motion_val[ xy + wrap + 1 ][ 1 ] = motion_y;
}
MPV_decode_mb();
if ( ++mb_x >= mbWidth ) {
mb_x = 0;
mb_y++;
if((mb_y << (field_pic?1:0)) >= mbHeight){
endOfSlice = true;
break;
}
}
/* Skip mb handling */
if ( mb_skip_run == -1 ) {
mb_skip_run = 0;
do {
code = in.getVLC( mbincr_vlc );
if ( code <= 33 ) mb_skip_run += code;
if ( code == 35 ) {
endOfSlice = true;
break;
}
} while ( code >= 33 );
}
}
in.seek( ((in.getPos()/8)-2)*8 );
}
/**
* Decode a picture frame
*/
private int lastFullFrame;
private boolean notConsumed = false;
private boolean findSequenceHeader = true;
public void decodeFrame( byte[] data, int size ) throws FFMpegException {
/**
* Manage data buffer
*/
if ( !notConsumed ) {
in.addData( data, 0, size );
int end = in.getPos() + in.availableBits() - size * 8;
/* Find full frame */
lastFullFrame = 0;
boolean sequenceFrame = false;
boolean dropThisFrame = false;
for ( int i = 0; i < size - 6; i++ ) {
/**
* Extract pointers to frames
*/
if ( data[ i ] == 0
&& data[ i + 1 ] == 0
&& data[ i + 2 ] == 1 ) {
byte header = data[ i + 3 ];
if ( header == (byte)PICTURE_START_CODE ) {
/* Picture start code *
int reference = ((currentData[ i + 4 ] & 0xff) << 2) | ( (currentData[ i + 5 ] >> 6 ) & 0x03 );
int pict_code = (currentData[ i + 5 ] >>3) & 0x7;
System.out.println( pictName[pict_code] + " " + reference );
*/
if ( !sequenceFrame ) {
lastFullFrame = i * 8 + end;
}
sequenceFrame = false;
}
if ( !sequenceFrame && (header == (byte)SEQ_START_CODE || header == (byte)GOP_START_CODE) ) {
sequenceFrame = true;
lastFullFrame = i * 8 + end;
findSequenceHeader = false;
}
}
}
}
if ( lastFullFrame == 0 || findSequenceHeader ) {
// System.out.println( "Insufficient data" + lastFullFrame);
pict_type = SKIP_FRAME_TYPE;
lastFullFrame = 0;
return;
}
notConsumed = false;
/**
* Until the end of frame (or we run out of data)
*/
boolean endOfFrame = false;
while ( !endOfFrame && in.availableBits() > 24 ) {
/* Find current header */
do {
if ( in.showBits( 24 ) == SYNC_BYTES ) {
in.getBits( 24 );
currentHeader = in.getBits( 8 );
} else {
in.getBits(8 -(in.getPos() %8));
currentHeader = -1;
}
} while ( currentHeader == -1 );
switch ( currentHeader ) {
case SEQ_START_CODE: {
// System.out.println( "SEQ_START_CODE" );
mpeg1_decode_sequence();
break;
}
case PICTURE_START_CODE: {
// System.out.println( "PICTURE_START_CODE" );
mpeg1_decode_picture();
break;
}
case EXT_START_CODE: {
// System.out.println( "EXT_START_CODE" );
mpeg_decode_extension();
break;
}
case USER_START_CODE: {
break;
}
case GOP_START_CODE: {
first_field = false;
break;
}
default: {
if (currentHeader >= SLICE_MIN_START_CODE &&
currentHeader <= SLICE_MAX_START_CODE) {
mpeg_decode_slice( currentHeader - SLICE_MIN_START_CODE );
if ( mb_y >= mbHeight ) {
endOfFrame = true;
// System.out.println( "EOF" );
}
}
break;
}
}
}
/* Find next header Sequence header*/
while ( in.showBits( 24 ) != SYNC_BYTES ) {
in.getBits(8 -(in.getPos() %8));
}
}
/**
* Retrieve the supported input formats. Currently "mpeg"
*
* @return Format[] the supported input formats
*/
public Format[] getSupportedInputFormats() {
return new Format[] { new VideoFormat( "mpeg" ) };
}
/**
* 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
*/
private VideoFormat inputFormat;
public Format setInputFormat( Format format ) {
inputFormat = (VideoFormat)format;
initialise( (int)inputFormat.getSize().getWidth(),
(int)inputFormat.getSize().getHeight() );
if ( inputFormat.getFrameRate() > 0 ) {
frameRate = inputFormat.getFrameRate();
}
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( showInterlace ? mbWidth * 32 : mbWidth * 16,
showInterlace ? mbHeight * 8 : mbHeight * 16 ),
-1, (new int[0]).getClass(), // array
inputFormat.getFrameRate(), // Frames/sec
32, 0xff0000, 0x00ff00, 0x0000ff) ; //Colours
}
/**
* Converts a byte array Buffer of Mpeg1/2 video data
* into an integer array Buffer of video data.
*
* Always output one frame.
*
* @return BUFFER_PROCESSED_OK The output buffer contains a valid frame
* @return BUFFER_PROCESSED_FAILED A decoding problem was encountered
*/
private long lastTime;
private int frames;
private int skipToIFrame = 0;
public int process( Buffer in, Buffer out ) {
/* Flush buffer */
if ( (in.getFlags() & Buffer.FLAG_FLUSH) != 0 ) reset();
/* Do we need to set the time in the output buffer */
out.setFlags( in.getFlags() );
if ( (in.getFlags() & Buffer.FLAG_NO_WAIT) == 0 ) {
/* Has the input buffer specified a new timestamp */
if ( lastTime != in.getTimeStamp() ) {
lastTime = in.getTimeStamp();
frames = 0;
} else {
/* Calculate time depending on frame rate */
out.setFlags( in.getFlags() | Buffer.FLAG_RELATIVE_TIME | Buffer.FLAG_NO_DROP );
}
out.setTimeStamp( in.getTimeStamp() + (long)(((long)1000000000)/frameRate) * frames);
}
try {
byte[] data = (byte[])in.getData();
decodeFrame( data, in.getLength() );
if ( pict_type == I_TYPE || pict_type == P_TYPE) {
/*
* P/I type - these become the "next" frame
*
* Display the last I or P frame we decoded
*/
displayOutput.showNextScreen(out);
displayOutput.endIPFrame();
} else if ( pict_type == B_TYPE ) {
/*
* B frame - simply show the current frame (it will be discarded)
*/
if ( !skipBFrames && !hurryUp ) {
displayOutput.showScreen(out);
displayOutput.endBFrame();
} else {
displayOutput.endBFrame();
out.setLength(0);
}
} else {
/**
* We do not have enough data for a full frame - freeze
*/
out.setLength(0);
numberOfFramesDelivered = 0;
return BUFFER_PROCESSED_OK;
}
/* Manage video discontinuity */
if ( pict_type == I_TYPE && skipToIFrame != 0 ) skipToIFrame--;
if ( pict_type == P_TYPE && skipToIFrame == 1 ) skipToIFrame--;
if ( skipToIFrame != 0 ) {
out.setLength(0);
out.setFlags( Buffer.FLAG_NO_WAIT );
frames = 0;
}
} catch( Error e ) {
e.printStackTrace();
this.in = new BitStream();
out.setLength(0);
return BUFFER_PROCESSED_OK;
} catch ( Exception e ) {
e.printStackTrace();
this.in = new BitStream();
// this.in.seek( this.in.getPos() + this.in.availableBits() );
out.setLength(0);
return BUFFER_PROCESSED_OK;
}
numberOfFramesDelivered++;
frames++;
if ( this.in.getPos() < lastFullFrame ) {
/* System.out.println( "End of frame at " + this.in.getPos()
+ ". Last frame is " + lastFullFrame + " "
+ (this.in.getPos() + this.in.availableBits())
+ " remaining " + this.in.availableBits()); */
notConsumed = true;
return INPUT_BUFFER_NOT_CONSUMED;
} else {
// System.out.println( "End of buffer at " + this.in.getPos() + ". Last frame is " + lastFullFrame + " " + Integer.toHexString( this.in.showBits(32) ) + " remaining " + this.in.availableBits());
hurryUp = false;
numberOfFramesDelivered = 0;
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() {
frames = 0;
in.seek( in.getPos() + in.availableBits() ) ;
skipToIFrame = 2;
}
/**
* Retrives the name of this video codec: "MPEG video decoder"
* @return Codec name
*/
public String getName() {
return "MPEG 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( "MPEG", size, -1, (new byte[ 0 ]).getClass(), 0 ) );
}
public void setEncoding( String encoding ) {
}
public void setIsRtp( boolean isRtp ) {
}
public void setIsTruncated( boolean isTruncated ) {
}
}