/*
* @(#)NativeDecoder.java 1.47 03/04/24
*
* Copyright (c) 1996-2003 Sun Microsystems, Inc. All rights reserved.
*/
package com.sun.media.codec.video.vcm;
import javax.media.*;
import javax.media.control.*;
import javax.media.Format;
import javax.media.format.VideoFormat;
import javax.media.format.RGBFormat;
import javax.media.format.YUVFormat;
import com.sun.media.format.AviVideoFormat;
import com.sun.media.*;
import com.sun.media.util.*;
import com.sun.media.vfw.*;
import java.util.Vector;
import java.awt.*;
public final class NativeDecoder extends BasicCodec
implements com.sun.media.util.DynamicPlugIn {
private static boolean loaded = false;
static {
com.sun.media.JMFSecurityManager.loadLibrary("jmvcm");
loaded = true;
}
static Integer processLock = new Integer(1);
protected VideoFormat inputFormat = null;
protected VideoFormat outputFormat = null;
/*
protected String [] knownEncodings = {//VideoFormat.CINEPAK,
"msvc", "cram",
"mrle", "vdec",
"vgpx",
//"rgb",
"i263", "i420",
"yuy2", "yvu9", "y411",
//"m263",
//VideoFormat.INDEO32,
VideoFormat.INDEO41,
VideoFormat.INDEO50};
*/
private static String [] supportedEncodings = null;
private int vcmHandle = 0;
private boolean debug = false;
private BitMapInfo biIn = null;
private BitMapInfo biOut = null;
private boolean dropFrame = false;
private boolean keepOutputRef = false;
private NBA outputRef = null;
public NativeDecoder() {
// Workaround for Netscape bug, where if the
// Class.forName fails the first time, because
// says native libraries couldn't be loaded,
// it succeeds the second time and
// throws an error later.
if (!loaded) {
throw new UnsatisfiedLinkError("Cannot load native libraries");
}
inputFormats = new VideoFormat[1];
inputFormats[0] = new VideoFormat(null);
outputFormats = new VideoFormat[1];
outputFormats[0] = new RGBFormat();
}
protected Format getInputFormat() {
return inputFormat;
}
protected Format getOutputFormat() {
return outputFormat;
}
private static synchronized void querySupportedEncodings() {
ICInfo icinfo = new ICInfo();
BitMapInfo biOut = new BitMapInfo();
int i = 0;
Vector listEncoding = new Vector();
while (true) {
icinfo = new ICInfo();
boolean result = VCM.icInfoEnum("vidc", i, icinfo);
if (!result || i > 30)
break;
else {
if (!(icinfo.fccHandler == null ||
icinfo.fccHandler.equalsIgnoreCase(VideoFormat.MJPG) ||
icinfo.fccHandler.equalsIgnoreCase(VideoFormat.CINEPAK)
)) {
int handle = VCM.icOpen("vidc", icinfo.fccHandler, VCM.ICMODE_DECOMPRESS);
if (handle != 0) {
if (icinfo.fccHandler.equalsIgnoreCase("ir41")) // hack
icinfo.fccHandler = "IV41";
if (icinfo.fccHandler.equalsIgnoreCase("ir50")) // hack
icinfo.fccHandler = "IV50";
if (icinfo.fccHandler.equalsIgnoreCase("ir32")) // hack
icinfo.fccHandler = "IV32";
listEncoding.addElement(icinfo.fccHandler.toUpperCase());
VCM.icClose(handle);
}
}
}
i++;
}
listEncoding.addElement("YVU9");
supportedEncodings = new String[listEncoding.size()];
for (i = 0; i < supportedEncodings.length; i++) {
supportedEncodings[i] = (String) listEncoding.elementAt(i);
}
}
public Format [] getSupportedInputFormats() {
if (supportedEncodings == null) {
querySupportedEncodings();
}
inputFormats = new VideoFormat[supportedEncodings.length];
for (int i = 0; i < supportedEncodings.length; i++) {
inputFormats[i] = new VideoFormat(supportedEncodings[i]);
}
return inputFormats;
}
public Format [] getSupportedOutputFormats(Format in) {
if (in == null)
return outputFormats;
if (matches(in, inputFormats) == null)
return new Format[0];
VideoFormat video = (VideoFormat) in;
Dimension size = video.getSize();
if (size == null)
size = new Dimension(320, 240);
RGBFormat [] tryFormats = new RGBFormat[] {
new RGBFormat(size, size.width * size.height * 3,
Format.byteArray,
video.getFrameRate(), // frame rate
24,
3, 2, 1,
3,
size.width * 3,
Format.TRUE,
Format.NOT_SPECIFIED), // endian
new RGBFormat(size, size.width * size.height * 3,
Format.byteArray,
video.getFrameRate(), // frame rate
24,
3, 2, 1,
3,
size.width * 3,
Format.FALSE, // flipped
Format.NOT_SPECIFIED), // endian
new RGBFormat(size, size.width * size.height,
Format.shortArray,
video.getFrameRate(), // frame rate
16,
0x07C00, 0x003e0, 0x0001F,
1, size.width,
Format.FALSE, // flipped
Format.NOT_SPECIFIED), // endian
new RGBFormat(size, size.width * size.height,
Format.shortArray,
video.getFrameRate(), // frame rate
16,
0x07C00, 0x003e0, 0x0001F,
1, size.width,
Format.TRUE,
Format.NOT_SPECIFIED) // endian
};
if (debug) System.err.println("Input format = " +
new BitMapInfo((VideoFormat)video));
Vector supportedOuts = new Vector();
int handle = VCM.icLocate("vidc", "RGB",
new BitMapInfo((VideoFormat) video),
null,
VCM.ICMODE_DECOMPRESS);
if (handle != 0) {
for (int enum = 0; enum < tryFormats.length; enum++) {
if ( VCM.icDecompressBegin(handle,
new BitMapInfo((VideoFormat) video),
new BitMapInfo(tryFormats[enum])) ) {
supportedOuts.addElement(tryFormats[enum]);
if (debug) System.err.println("VCM " + video.getEncoding() + " supports " + tryFormats[enum]);
VCM.icDecompressEnd(handle);
} else {
}
}
VCM.icClose(handle);
}
Format [] outs = new Format[supportedOuts.size()];
for (int i = 0; i < outs.length; i++) {
outs[i] = (Format) supportedOuts.elementAt(i);
}
//if (debug) System.err.println("VCM.getSupportedOutputFormats: " + rgb);
return outs;
}
public Format setInputFormat(Format in) {
if (supportedEncodings == null) {
querySupportedEncodings();
}
String encoding = in.getEncoding();
if ( encoding.equalsIgnoreCase(VideoFormat.CINEPAK) ||
encoding.equalsIgnoreCase(VideoFormat.MJPG) ||
encoding.equalsIgnoreCase("RGB") )
return null;
if ( encoding.equalsIgnoreCase("mpg4") ||
encoding.equalsIgnoreCase("mp42") ||
encoding.equalsIgnoreCase("mp43") ||
encoding.equalsIgnoreCase("iv32") )
keepOutputRef = true;
else
keepOutputRef = false;
if (in instanceof VideoFormat) {
if (matches(in, inputFormats) != null) {
inputFormat = (VideoFormat) in;
if (opened) {
try {
close();
Format [] ofs =
getSupportedOutputFormats(inputFormat);
if (ofs.length < 1)
return null;
else if (ofs.length == 1)
outputFormat = (RGBFormat) ofs[0];
else {
for (int i = 0; i < ofs.length; i++) {
if (((RGBFormat)outputFormat).getBitsPerPixel() ==
((RGBFormat)ofs[i]).getBitsPerPixel() &&
((RGBFormat)outputFormat).getFlipped() ==
((RGBFormat)ofs[i]).getFlipped()) {
outputFormat = (VideoFormat) ofs[i];
break;
}
}
}
open();
} catch (Exception e) {
return null;
}
}
return in;
}
}
return null;
}
public Format setOutputFormat(Format out) {
if (out instanceof RGBFormat) {
outputFormat = (RGBFormat) out;
if (debug) System.err.println("VCM.setOutputFormat : " + outputFormat);
return out;
}
return null;
}
public void open() throws ResourceUnavailableException {
if (inputFormat == null || outputFormat == null)
throw new ResourceUnavailableException("Formats not set!");
Dimension size = inputFormat.getSize();
//Object bi = inputFormat.getExtraData();
Object bi = null;
String inFourCC = inputFormat.getEncoding();
biOut = new BitMapInfo();
// Translate from AviVideoFormat to BitMapInfo
if (inputFormat instanceof AviVideoFormat) {
bi = new BitMapInfo(inputFormat);
if (debug) System.err.println("VCM.open, BMI = " + bi);
}
if (bi != null && bi instanceof BitMapInfo) {
biIn = (BitMapInfo) bi;
} else
biIn = new BitMapInfo(inFourCC, size.width, size.height);
String cramHack = inFourCC;
if (cramHack.equalsIgnoreCase("cram"))
cramHack = "msvc";
vcmHandle = VCM.icLocate("vidc", "RGB",
biIn, null,
VCM.ICMODE_DECOMPRESS);
if (debug) System.err.println("open.vcmHandle for " + inFourCC + " = " + vcmHandle);
if (vcmHandle == 0)
throw new ResourceUnavailableException("Could not open VCM driver");
BitMapInfo testOut = new BitMapInfo();
//VCM.icDecompressGetFormat(vcmHandle, biIn, testOut);
if (debug) System.err.println("open.preferred out = " + testOut);
// biOut = testOut;
// new trial
biOut = new BitMapInfo(outputFormat);
if (debug) System.err.println("open.requested out = " + biOut);
boolean tryFlip = true;
boolean result;
result = VCM.icDecompressBegin(vcmHandle, biIn, biOut);
if (result == false) {
if (debug) System.err.println("VCM.open : Could not set i/o format");
close();
throw new ResourceUnavailableException("Formats not supported");
}
if (debug) System.err.println("VCM.open success!");
super.open();
}
public void close() {
if (vcmHandle != 0)
VCM.icClose(vcmHandle);
vcmHandle = 0;
super.close();
}
public void reset() {
if (vcmHandle != 0) {
VCM.icDecompressEnd(vcmHandle);
VCM.icDecompressBegin(vcmHandle, biIn, biOut);
}
}
public int process(Buffer inBuffer, Buffer outBuffer) {
if (isEOM(inBuffer)) {
propagateEOM(outBuffer);
return BUFFER_PROCESSED_OK;
}
Format outf = outBuffer.getFormat();
outBuffer.setFormat(outputFormat);
Object outBuf = validateData(outBuffer, 0, true);
int bufType = 1;
if (outputFormat.getDataType() == Format.shortArray) {
bufType = 2;
}
Object inBuf = getInputData(inBuffer);
long flags = 0;
int size = inBuffer.getLength();
if (size < 2)
return BUFFER_PROCESSED_FAILED;
Object header = inBuffer.getHeader();
if (header != null && header instanceof Integer) {
flags = ((Integer)header).intValue();
}
if (dropFrame)
flags |= 0x20000000L;
biIn.biSizeImage = size;
// biOut.biHeight = -outputFormat.getSize().height;
int result;
synchronized (processLock) {
long outBytes = getNativeData(outBuf);
long inBytes = getNativeData(inBuf);
if (!keepOutputRef) {
result = VCM.icDecompress(vcmHandle, flags,
biIn, inBuf, inBytes, biOut, outBuf, outBytes,
bufType);
} else {
if (outputRef == null)
outputRef = new NBA(byte[].class, outputFormat.getMaxDataLength());
result = VCM.icDecompress(vcmHandle, flags, biIn, inBuf, inBytes,
biOut, null,
outputRef.getNativeData(), bufType);
if (outBytes != 0) {
outputRef.copyTo((NBA)outBuf);
} else {
outputRef.copyTo((byte[])outBuf);
}
}
}
if (dropFrame)
outBuffer.setDiscard(true);
else
outBuffer.setLength(outputFormat.getMaxDataLength());
if (result < 0)
return BUFFER_PROCESSED_FAILED;
else
return BUFFER_PROCESSED_OK;
}
private Control [] controls = null;
private DC dc = null;
public Object [] getControls() {
if (dc == null) {
dc = new DC();
controls = new Control[1];
controls[0] = dc;
}
return controls;
}
public String getName() {
return "VCM Decoder";
}
void dropNextFrame(boolean on) {
dropFrame = on;
}
public Format [] getBaseInputFormats() {
Format [] formats = new Format[1];
formats[0] = new VideoFormat(null);
return formats;
}
public Format [] getBaseOutputFormats() {
Format [] formats = new Format[1];
formats[0] = new RGBFormat();
return formats;
}
/****************************************************************
* Decoder Control
****************************************************************/
class DC implements FrameProcessingControl, QualityControl {
public Component getControlComponent() {
return null;
}
public boolean setMinimalProcessing(boolean on) {
dropNextFrame(true);
return true;
}
/**
* Informs the codec that it is behind by some number of frames
* and that it needs to either speed up by dropping quality or by
* dropping frames as it sees fit. The value <code>framesBehind</code>
* can either be positive, zero or negative. A negative value indicates
* that the codec is ahead by that many frames and can possibly improve
* quality if its not at maximum. This method needs to be called before a
* call to <code>process</code>. The value is remembered by the codec
* until it is explicitly changed again.
*/
public void setFramesBehind(float framesBehind) {
if (framesBehind > 0)
dropNextFrame(true);
else
dropNextFrame(false);
}
public int getFramesDropped() {
return 0; ///XXX not implemented
}
/**
* Set the quality for the decoding or encoding. This value may have
* different
* effects depending on the type of compression. A higher quality
* setting will result in better quality of the resulting bits, e.g.
* better image quality for video. There is usually a tradeoff between
* CPU usage and the quality; in that higher quality requires higher
* CPU usage. This value is
* only a hint and the codec can choose to ignore it. The actual value
* that was set is returned<p>.
* It should be in the range of 0.0 to 1.0.
* @see #getQuality
*/
public float setQuality(float quality) {
return 1.0f;
}
/**
* Returns the current value of the compression quality parameter.
*/
public float getQuality() {
return 1.0f;
}
/**
* Return the default compression quality recommended for
* this codec.
*/
public float getPreferredQuality() {
return 1.0f;
}
public boolean isTemporalSpatialTradeoffSupported() {
return true;
}
}
}
|