FileDocCategorySizeDatePackage
NativeEncoder.javaAPI DocJMF 2.1.1e13850Mon May 12 12:20:46 BST 2003com.sun.media.codec.video.jpeg

NativeEncoder

public class NativeEncoder extends BasicCodec

Fields Summary
private RGBFormat
inputFormat
private VideoFormat
outputFormat
private static boolean
loaded
private static boolean
canLoad
private int
peer
private static int
QSCALE
private float
quality
private float
prevQuality
private boolean
isMotionJPEG
private boolean
firstFrame
private int
PACKET_SIZE
private int
currentSeq
private long
timestamp
private byte[]
rtp_data
static Integer
processLock
private int
copyLength
private boolean
newframe
private int
current_offset
int
returnVal
private boolean
dropFrame
private boolean
minimal
private static final int
DEFAULT_FRAMERATE
private byte[]
mjpgExtraBytes
Constructors Summary
public NativeEncoder()
Codec Methods


           

    // Initialize default formats.
      
	inputFormats = new RGBFormat[1];
	inputFormats[0] = new RGBFormat();
	outputFormats = new VideoFormat[2];
	outputFormats[0] = new JPEGFormat();
	outputFormats[1] = new VideoFormat(VideoFormat.MJPG);
	class QCA extends QualityAdapter implements Owned {
	    public QCA() {
		super(0.6f, 0f, 1f, true);
	    }
	    
	    public float setQuality(float newValue) {
		quality = super.setQuality(newValue);
		return quality;
	    }

	    public String getName() {
		return "JPEG Quality";
	    }

	    public Object getOwner() {
		return NativeEncoder.this;
	    }
	}

	QualityControl qualityControl = new QCA();

	FrameProcessingControl fpc = new FrameProcessingControl() {
	    public boolean setMinimalProcessing(boolean newMinimal) {
		minimal = newMinimal;
		return minimal;
	    }

	    public void setFramesBehind(float frames) {
		if (frames >= 1)
		    dropFrame = true;
		else
		    dropFrame = false;
	    }

	    public Component getControlComponent() {
		return null;
	    }

            public int getFramesDropped() {
                return 0;       ///XXX not implemented
            }

	};

	controls = new Control[2];
	controls[0] = qualityControl;
	controls[1] = fpc;
    
Methods Summary
public synchronized voidclose()

	if (peer != 0)
	    freeJPEGEncoder(peer);
	peer = 0;
	super.close();
    
private native intencodeJPEG(int peer, java.lang.Object inData, long inBytes, int width, int height, byte[] outData, int length, int quality, int decimation, boolean flipped)

public voidfinalize()

	close();
    
private native booleanfreeJPEGEncoder(int peer)

protected javax.media.FormatgetInputFormat()

	return inputFormat;
    
public java.lang.StringgetName()

	return "JPEG Encoder";
    
protected javax.media.FormatgetOutputFormat()

	return outputFormat;
    
public javax.media.Format[]getSupportedOutputFormats(javax.media.Format in)

	if (in == null)
	    return outputFormats;

	// Make sure the input is RGB video format
	if (!verifyInputFormat(in))
	    return new Format[0];

	Format out [] = new Format[2];
	RGBFormat rgb = (RGBFormat) in;
	Dimension size = rgb.getSize();
	int maxDataLength = size.width * size.height * 3;
	VideoFormat jpeg = new JPEGFormat(size, maxDataLength,
					  Format.byteArray,
					  rgb.getFrameRate(),
					  // -- hsy for bugid 4414481
					  //(int) (qfToQuality(quality)),
					  //qfToType(quality));
					  Format.NOT_SPECIFIED,
					  Format.NOT_SPECIFIED);
	VideoFormat mjpg = new AviVideoFormat(VideoFormat.MJPG,
					      size, maxDataLength,
					      Format.byteArray,
					      rgb.getFrameRate(),
					      1, 24, maxDataLength,
					      0, 0, 0, 0,
					      mjpgExtraBytes);
	out[0] = jpeg;
	out[1] = mjpg;
	
	return out;
    
private native intinitJPEGEncoder(int width, int height, int quality, int decimation)
Native Methods

public static voidmain(java.lang.String[] args)
Test Code

	int width = 320;
	int height = 240;
	RGBFormat in = new RGBFormat(new Dimension(width, height),
				     width * height * 3,
				     Format.byteArray,
				     Format.NOT_SPECIFIED, // frame rate
				     24,
				     1, 2, 3, 3, width * 3,
				     Format.FALSE, // flipped
				     Format.NOT_SPECIFIED // endian
				     );
	VideoFormat out = new VideoFormat(VideoFormat.JPEG,
					  new Dimension(width, height),
					  width * height * 3,
					  Format.byteArray,
					  // frame rate
					  Format.NOT_SPECIFIED
					  );

	NativeEncoder e = new NativeEncoder();
	if (e.setInputFormat(in) != null) {
	    if (e.setOutputFormat(out) != null) {
		try {
		    e.open();
		} catch (ResourceUnavailableException rue) {
		    System.err.println("Couldn't open encoder");
		    System.exit(0);
		}

		byte [] rgbData  = new byte[width * height * 3];

		System.err.println("Filling rgb data");
		for (int i = 0; i < width * height * 3; i++)
		    rgbData[i] = (byte) (((i % 3) * (i % width)) % 255);

		System.err.println("Encoding");
		
		Buffer inBuffer = new Buffer();
		inBuffer.setFormat(in);
		inBuffer.setData(rgbData);
		inBuffer.setLength(width * height * 3);

		Buffer outBuffer = new Buffer();
		outBuffer.setFormat(out);

		int result = e.process(inBuffer, outBuffer);
		System.err.println("Result = " + result);
		e.close();
	    }
	
	}

	System.exit(0);
    
public voidopen()

	if (!canLoad)
	    throw new ResourceUnavailableException("Unable to load" +
						   " native JPEG converter");

	// Size restriction - 8x8
	Dimension size = inputFormat.getSize();
	if (size == null || (size.width % 8) != 0 || (size.height % 8) != 0 ) {
	    Log.error("Class: " + this);
	    Log.error("  can only encode in sizes of multiple of 8 pixels.");
	    throw new ResourceUnavailableException("Unable to encode in size " + size);
	}

	if (!loaded) {
	    try {
		JMFSecurityManager.loadLibrary( "jmutil");
		JMFSecurityManager.loadLibrary( "jmjpeg");
		loaded = true;
	    } catch (Throwable t) {
		canLoad = false;
		throw new ResourceUnavailableException("Unable to load " +
						       "native JPEG encoder");
	    }
	}

	if (inputFormat == null || outputFormat == null)
	    throw new ResourceUnavailableException("Formats not set " +
						   "on the JPEG encoder");

	if (peer != 0)
	    close();
	try {
	    peer = initJPEGEncoder(size.width, size.height,
				   (int) qfToQuality(quality),
				   qfToDecimation(quality));
	    firstFrame = true;
	} catch (Throwable t) {
	}
	
	if (peer == 0)
	    throw new ResourceUnavailableException("Unable to initialize JPEG encoder");
	super.open();
    
public synchronized intprocess(javax.media.Buffer inBuffer, javax.media.Buffer outBuffer)

	Object header = null;
	float changeQuality;
	int changeDecimation;
	Format inFormat;
	Object inData;
	long inBytes;
	boolean flipped;

	// EndOfMedia?
	if (isEOM(inBuffer)) {
	    propagateEOM(outBuffer);
	    return BUFFER_PROCESSED_OK;
	}

	// Dropping frames?
	if (minimal || dropFrame) {
	    outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_DISCARD);
	    return BUFFER_PROCESSED_OK;
	}
	
	inFormat = inBuffer.getFormat();
	inData = getInputData(inBuffer);
	inBytes = getNativeData(inData);
	flipped = ((RGBFormat) inFormat).getFlipped() == Format.TRUE;

	//if (outputFormat.getEncoding().equals(VideoFormat.JPEG)){

	byte [] outData = (byte[]) outBuffer.getData();
	if (outData == null ||
	    outData.length < outputFormat.getMaxDataLength()) {
	    outData = new byte[outputFormat.getMaxDataLength()];
	    outBuffer.setData(outData);
	}

	outBuffer.setFormat(outputFormat);
	    
	if (prevQuality != quality) {
	    prevQuality = quality;
	    changeQuality = qfToQuality(quality);
	    outputFormat = new JPEGFormat(outputFormat.getSize(),
					  outputFormat.getMaxDataLength(),
					  Format.byteArray,
					  outputFormat.getFrameRate(),
					  (int) qfToQuality(quality),
					  qfToType(quality));
	    close();
	    // Allow changing decimation only if this is the first encoded frame
	    if (firstFrame) 
		changeDecimation = qfToDecimation(quality);
	    else
		changeDecimation = -1;
	} else { // dont change quality
	    changeQuality = -1;
	    changeDecimation = -1;
	}

	if (peer == 0) {
	    try {
		open();
	    } catch (ResourceUnavailableException re) {
		return BUFFER_PROCESSED_FAILED;
	    }
	}

	Dimension size = inputFormat.getSize();
	synchronized (processLock) {
	    returnVal =
		encodeJPEG(peer,
			   inData,
			   inBytes,
			   size.width,
			   size.height,
			   outData,
			   outData.length,
			   (int) changeQuality,
			   (int) changeDecimation,
			   flipped);
	}
	firstFrame = false;
	if (returnVal > 0) {
	    outBuffer.setLength(returnVal);
	    outBuffer.setOffset(0);
	    inBuffer.setLength(0);
	    outBuffer.setFlags(Buffer.FLAG_KEY_FRAME);
	    outBuffer.setTimeStamp(inBuffer.getTimeStamp());
	    outBuffer.setFormat(outputFormat);
	    return BUFFER_PROCESSED_OK;
	}
	outBuffer.setDiscard(true);
	return BUFFER_PROCESSED_FAILED;
    
protected intqfToDecimation(float quality)

	if (isMotionJPEG)
	    return 2;
	
	if (quality >= 0.8f) {
	    if (quality >= 0.90f)
		return 4;
	    else
		return 2;
	} else
	    return 1;
    
protected floatqfToQuality(float quality)


        
	if (quality < 0.1f)
	    quality = 0.1f;
	if (isMotionJPEG)
	    return quality * (float) QSCALE;
	if (quality < 0.8f) {
	    return quality * 1.25f * QSCALE;
	} else {
	    return (float) QSCALE;
	}
    
protected intqfToType(float quality)

	switch (qfToDecimation(quality)) {
	case 1:
	    return JPEGFormat.DEC_420;
	case 2:
	    return JPEGFormat.DEC_422;
	case 4:
	    return JPEGFormat.DEC_444;
	}
	return 1;
    
public voidreset()

	// Anything to do?
    
public javax.media.FormatsetInputFormat(javax.media.Format input)

	if (!verifyInputFormat(input))
	    return null;

	inputFormat = (RGBFormat) input;
	if (opened) {
	    close();
	    Dimension size = inputFormat.getSize();
	    int maxDataLength = size.width * size.height * 3;
	    if (outputFormat instanceof JPEGFormat) 
		outputFormat = new JPEGFormat(size,
					      maxDataLength,
					      Format.byteArray,
					      inputFormat.getFrameRate(),
					      (int) qfToQuality(quality),
					      qfToType(quality));
	    else
		outputFormat = new AviVideoFormat(VideoFormat.MJPG,
						  size, maxDataLength,
						  Format.byteArray,
						  inputFormat.getFrameRate(),
						  1, 24, maxDataLength,
						  0, 0, 0, 0,
						  mjpgExtraBytes);
	}	    
	return input;
    
public javax.media.FormatsetOutputFormat(javax.media.Format output)

	if (matches(output, outputFormats) == null){
	    return null;
	}
	outputFormat = (VideoFormat) output;
	if (outputFormat.getEncoding().equalsIgnoreCase(VideoFormat.MJPG))
	    isMotionJPEG = true;
	else
	    isMotionJPEG = false;
	return output;
    
private booleanverifyInputFormat(javax.media.Format input)

	if (!(input instanceof RGBFormat))
	    return false;
	RGBFormat rgb = (RGBFormat) input;
	if ( rgb.getDataType() != Format.byteArray ||
	     rgb.getBitsPerPixel() != 24 ||
	     rgb.getRedMask() != 3 ||
	     rgb.getGreenMask() != 2 ||
	     rgb.getBlueMask() != 1 ||
	     rgb.getSize() == null ||
	     rgb.getLineStride() < rgb.getSize().width ||
	     rgb.getPixelStride() != 3 )
	    return false;
	return true;