/***************************************
* ViPER-MPEG *
* The Video Processing *
* Evaluation Resource *
* MPEG-1 Decoder *
* Distributed under the LGPL license *
* Terms available at gnu.org. *
* *
* Copyright University of Maryland, *
* College Park. *
***************************************/
package edu.umd.cfar.lamp.mpeg1.video;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.MemoryImageSource;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import edu.umd.cfar.lamp.mpeg1.MpegException;
public class VideoDecoder
{
private DecoderState decoderState = null;
private VideoIndex videoIndex = new VideoIndex(this);
private VideoSource videoSource = null;
private int currentFrame = -1;
public VideoDecoder(VideoSource videoSource) throws IOException, MpegException
{
this.videoSource = videoSource.copySource();
}
public int getStreamID()
{
return videoSource.getStreamID();
}
public void writeIndex(DataOutput out) throws IOException, MpegException
{
index();
videoIndex.writeIndex(out);
}
public void readIndex(DataInput in) throws IOException, MpegException
{
videoIndex.readIndex(in);
}
public VideoSource getVideoSource()
{
return videoSource;
}
private void initDecoderState() throws IOException, MpegException
{
if (decoderState == null)
{
getFirstSequenceHeader();
decoderState = new DecoderState(this);
}
}
public SequenceHeader getSequenceHeader(int frame) throws IOException, MpegException
{
index();
return videoIndex.getSequenceHeader(frame);
}
private void getFirstSequenceHeader() throws IOException, MpegException
{
videoIndex.getFirstSequenceHeader();
}
public int getFrameWidth() throws IOException, MpegException
{
getFirstSequenceHeader();
return videoIndex.getFrameWidth();
}
public int getFrameHeight() throws IOException, MpegException
{
getFirstSequenceHeader();
return videoIndex.getFrameHeight();
}
public int getBitRate() throws IOException, MpegException
{
getFirstSequenceHeader();
return videoIndex.getBitRate();
}
public PelAspectRatio getPixelAspectRatio() throws IOException, MpegException
{
getFirstSequenceHeader();
return videoIndex.getPixelAspectRatio();
}
public float getFrameRate() throws IOException, MpegException
{
getFirstSequenceHeader();
return videoIndex.getFrameRate();
}
public int getNumFrames() throws IOException, MpegException
{
index();
return videoIndex.getNumFrames();
}
public int getCurrentFrame()
{
return currentFrame;
}
public void seek(int frame) throws IOException, MpegException
{
if (frame < 0)
throw new FrameNotFoundException();
index();
initDecoderState();
if (frame != getCurrentFrame())
{
decoderState.seek(frame);
currentFrame = frame;
}
}
public long getPosition() throws IOException, MpegException
{
return getPosition(getCurrentFrame());
}
public long getPosition(int frame) throws IOException, MpegException
{
index();
return videoIndex.getPositionOfFrame(frame);
}
public int getPictureCodingType() throws IOException, MpegException
{
return getPictureCodingType(getCurrentFrame());
}
public int getPictureCodingType(int frame) throws IOException, MpegException
{
index();
return videoIndex.getPictureCodingTypeOfFrame(frame);
}
public int getLastIOrPFrame() throws IOException, MpegException
{
return getLastIOrPFrame(getCurrentFrame());
}
public int getLastIOrPFrame(int frame) throws IOException, MpegException
{
index();
return videoIndex.getLastIOrPFrame(frame);
}
public void index() throws IOException, MpegException
{
videoIndex.index();
}
public Image getImage() throws IOException, MpegException
{
int pixelData[] = convertYCbCrToRGB(decoderState.getCurrentYCbCr(),null);
return Toolkit.getDefaultToolkit().createImage(new MemoryImageSource(getFrameWidth(),getFrameHeight(),pixelData,0,getFrameWidth()));
}
public class VideoImage {
public int[] pixels;
public int width;
public int height;
}
public VideoImage getImageOptimize(int[] output) throws IOException, MpegException{
VideoImage toRet = new VideoImage();
toRet.pixels = convertYCbCrToRGB(decoderState.getCurrentYCbCr(),output);
toRet.width = getFrameWidth();
toRet.height = getFrameHeight();
return toRet;
}
private int[] convertYCbCrToRGB(int pixelData[], int[] result)
{
if (result==null){
result = new int[pixelData.length];
}
if (result.length!=pixelData.length){
throw new RuntimeException();
}
for (int i = 0; i < result.length; i++)
{
result[i] = convertYCbCrToRGB(pixelData[i]);
}
return result;
}
/**
* Converts YCbCr pixel data into RGB pixel data.
* @param packedYCbCrValue YCbCr pixel value. First byte is discarded, second byte is Y, third byte is Cb, fourth byte is Cr.
* @return rgb value
*/
private int convertYCbCrToRGB(int packedYCbCrValue)
{
int Y = (packedYCbCrValue & 0x00FF0000) >>> 16;
int Cb = (packedYCbCrValue & 0x0000FF00) >>> 8;
int Cr = (packedYCbCrValue & 0x000000FF);
return convertYCbCrToRGB(Y, Cb, Cr);
}
/**
* Converts YCbCr pixel data into RGB pixel data.
* @param Y value
* @param Cb value
* @param Cr value
* @return the RGB value
*/
private int convertYCbCrToRGB(int Y, int Cb, int Cr)
{
float Ycalc = (1.164f * (Y - 16));
int Cbcalc = (Cb - 128);
int Crcalc = (Cr - 128);
int red = (int)(Ycalc + 1.402f * Crcalc);
int green = (int)(Ycalc - 0.34414f * Cbcalc - 0.71414f * Crcalc);
int blue = (int)(Ycalc + 1.772f * Cbcalc);
red = clamp(red);
green = clamp(green);
blue = clamp(blue);
return packPixel(red, green, blue);
}
/**
* Constrains color component values to the 0..255 range.
* @param componentValue the value to clamp
* @return the clamped value
*/
public static int clamp(int componentValue)
{
return (componentValue < 0) ? 0 : (componentValue > 255) ? 255 : componentValue;
}
/**
* Packs three color components into one integer. Color
* components should be <code>clamp()</code>ed.
* @param component1 the first color channel value
* @param component2 the second color channel value
* @param component3 the third color channel value
* @return the packed color
*/
private int packPixel(int component1, int component2, int component3)
{
return (int)(0xFF000000 | (component1 << 16) | (component2 << 8) | (component3));
}
}