FileDocCategorySizeDatePackage
NativeEncoder.javaAPI DocJMF 2.1.1e9264Mon May 12 12:21:20 BST 2003com.sun.media.codec.video.vcm

NativeEncoder.java

/*
 * @(#)NativeEncoder.java	1.23 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.Format;
import javax.media.format.*;
import javax.media.control.QualityControl;
import com.sun.media.*;
import com.sun.media.controls.QualityAdapter;
import com.sun.media.vfw.*;
import java.util.Vector;
import java.awt.*;


public final class NativeEncoder extends BasicCodec {

    protected RGBFormat inputFormat = null;
    protected VideoFormat outputFormat = null;

    static {
	com.sun.media.JMFSecurityManager.loadLibrary("jmvcm");
    }

    private static String [] supportedEncodings = null;
    private static BitMapInfo [] supportedOutBMIs = null;
    
    private int vcmHandle = 0;

    private boolean debug = false;

    private BitMapInfo biIn = null;
    private BitMapInfo biOut = null;
    private BitMapInfo biPrev = null;
    private boolean dropFrame = false;

    private boolean hasQuality = false;
    private boolean hasCrunch = false;
    private boolean hasTemporal = false;
    private boolean hasFastCompress = false;

    private boolean begun = false;
    private QualityControl qc = null;
    private Control [] controls = null;
    private float quality = 9000f;
    final static int AVIF_KEYFRAME = 0x00000010;
    
    
    public NativeEncoder() {
	inputFormats = new VideoFormat[1];
	inputFormats[0] = new RGBFormat();

	outputFormats = new VideoFormat[1];
	outputFormats[0] = new VideoFormat(null);
    }

    protected Format getInputFormat() {
	return inputFormat;
    }

    protected Format getOutputFormat() {
	return outputFormat;
    }

    public String getName() {
	return "VCM Encoder";
    }

    private static synchronized void querySupportedEncodings() {

	ICInfo icinfo = new ICInfo();
	BitMapInfo biOut = new BitMapInfo();
	int i = 0;
	Vector listEncoding = new Vector();
	Vector listBMI = new Vector();
	
	while (true) {
	    boolean result = VCM.icInfoEnum("vidc", i, icinfo);
	    if (!result || i > 30)
		break;
	    else {
		int handle = VCM.icOpen("vidc", icinfo.fccHandler, VCM.ICMODE_COMPRESS);
		if (handle != 0) {
		    BitMapInfo biIn = new BitMapInfo("RGB", 320, 240);
		    biOut = new BitMapInfo("RGB", 320, 240);
		    VCM.icCompressGetFormat(handle, biIn, biOut);
		    if (biOut.fourcc.equalsIgnoreCase(icinfo.fccHandler) &&
			!(icinfo.fccHandler.equalsIgnoreCase(VideoFormat.MJPG) ||
			  icinfo.fccHandler.equalsIgnoreCase("dmb1"))) {
			listEncoding.addElement(biOut.fourcc);
			listBMI.addElement(biOut);
		    }
		    VCM.icClose(handle);
		}
	    }
	    i++;
	}

	supportedEncodings = new String[listEncoding.size()];
	supportedOutBMIs = new BitMapInfo[listEncoding.size()];
	for (i = 0; i < supportedEncodings.length; i++) {
	    supportedEncodings[i] = (String) listEncoding.elementAt(i);
	    supportedOutBMIs[i] = (BitMapInfo) listBMI.elementAt(i);
	}
    }

    public Format [] getSupportedInputFormats() {
	inputFormats[0] = new RGBFormat(null,
					Format.NOT_SPECIFIED,
					Format.byteArray,
					Format.NOT_SPECIFIED, // frame rate
					24,
					3, 2, 1,
					3,
					Format.NOT_SPECIFIED,
					Format.TRUE,
					Format.NOT_SPECIFIED); // endian

	return inputFormats;
    }

    public Format [] getSupportedOutputFormats(Format input) {
	if (input == null || !(input instanceof RGBFormat))
	    return new VideoFormat[] {
	           new VideoFormat(null)
	    };
	if (input.matches(inputFormats[0])) {
	    if (supportedEncodings == null) {
		querySupportedEncodings();
	    }

	    Dimension frameSize = ((VideoFormat)input).getSize();
	    if (frameSize == null)
		frameSize = new Dimension(320, 240);
	    outputFormats = new VideoFormat[supportedEncodings.length];
	    
	    for (int i = 0; i < supportedEncodings.length; i++) {
		int area = frameSize.width * frameSize.height;
		int outSize = area * supportedOutBMIs[i].biBitCount / 8;
		if (outSize < area)
		    outSize = area;
		outputFormats[i] = new VideoFormat(supportedEncodings[i],
						   frameSize,
						   outSize,
						   Format.byteArray,
						   ((VideoFormat)input).getFrameRate());
	    }
	    return outputFormats;  
	} else
	    return new Format[0];
    }

    public Format setInputFormat(Format in) {
	if (in instanceof RGBFormat) {
	    if (matches(in, inputFormats) != null) {
		inputFormat = (RGBFormat) in;
		if (begun) {
		    close();
		    try {
			// Update outputsize
			Dimension frameSize = ((RGBFormat)in).getSize();
			int area = frameSize.width * frameSize.height;
			int outSize = area * 24 / 8;
			outputFormat =
			    new VideoFormat(outputFormat.getEncoding(),
					    frameSize,
					    outSize,
					    Format.byteArray,
					    inputFormat.getFrameRate());
			
			open();
		    } catch (Exception e) {
			return null;
		    }
		}
		return in;
	    } else {
		//System.err.println("VCMEncoder didn't match:\n" + in);
	    }
	}

	return null;
    }

    public Format setOutputFormat(Format out) {
	if (out instanceof VideoFormat) {
	    outputFormat = (VideoFormat) out;
	    if (debug) System.err.println("VCM.setOutputFormat : "  + outputFormat);
	    return out;
	}
	
	return null;
    }

    public synchronized void open() throws ResourceUnavailableException {
	
	if (inputFormat == null || outputFormat == null)
	    throw new ResourceUnavailableException("Unknown formats");
	
	// Size restriction 4x4
	Dimension size = inputFormat.getSize();
	if (size == null || (size.width % 4) != 0 || (size.height % 4) != 0) {
	    Log.error("Class: " + this);
	    Log.error("  can only encode in sizes of multiple of 4 pixels.");
	    throw new ResourceUnavailableException("Wrong size.");
	}

	if (vcmHandle != 0)
	    close();

	biIn = new BitMapInfo(inputFormat);
	biOut = new BitMapInfo(outputFormat);

	vcmHandle = VCM.icLocate("vidc", VideoFormat.RGB,
				 biIn, biOut,
				 VCM.ICMODE_COMPRESS);
	if (vcmHandle == 0) {
	    throw new ResourceUnavailableException("Couldn't create compressor");
	}
	
	ICInfo icinfo = new ICInfo();
	VCM.icGetInfo(vcmHandle, icinfo);

	// Get properties of the compressor
	int flags = icinfo.dwFlags;
	//	System.err.println("Flags = " + flags);
	hasQuality = (flags & VCM.VIDCF_QUALITY) != 0;
	hasCrunch  = (flags & VCM.VIDCF_CRUNCH) != 0;
	hasTemporal = (flags & VCM.VIDCF_TEMPORAL) != 0;
	hasFastCompress = (flags & VCM.VIDCF_FASTC) != 0;

	begun = false;


	BitMapInfo testBMI = new BitMapInfo();
	VCM.icCompressGetFormat(vcmHandle, biIn, testBMI);
	biOut = testBMI;
	
	if (!VCM.icCompressBegin(vcmHandle, biIn, biOut)) {
	    VCM.icClose(vcmHandle);
	    throw new ResourceUnavailableException("Couldn't create compressor");
	} else {
	    begun = true;
	}
	seqNo = (seqNo + 3) & ~3;
    }

    public synchronized void close() {
	if (vcmHandle != 0) {
	    if (begun)
		VCM.icCompressEnd(vcmHandle);
	    VCM.icClose(vcmHandle);
	    begun = false;
	}
	vcmHandle = 0;
    }

    public Object [] getControls() {
	//System.err.println("NativeEncoder.getControls$$$$$$$");
	if (controls == null) {
	    //System.err.println("Has quality = " + hasQuality);
	    if (qc == null) {
		qc = new QualityAdapter(quality, 0f, 10000f, true) {
		    public float setQuality(float newValue) {
			quality = newValue;
			return super.setQuality(newValue);
		    }
		    
		    protected String getName() {
			return "Video Encoder Quality";
		    }
		};
		controls = new Control[1];
		controls[0] = qc;
	    } else {
		controls = new Control[0];
	    }
	}
	//System.err.println(controls);
	return controls;
    }
    

    int seqNo = 0;
    int keyFrameInterval = 4;
    int [] outFlags = new int[1];
    int [] ckid = new int[1];
    byte [] dataPrev = null;
    
    public int process(Buffer inBuffer, Buffer outBuffer) {
	if (isEOM(inBuffer)) {
	    propagateEOM(outBuffer);
	    return BUFFER_PROCESSED_OK;
	}

	int inFlags = 0;
	int outBufFlags = 0;
	
	Object outData = outBuffer.getData();

	if (outData == null || !(outData instanceof byte[]) ||
	    ((byte[])outData).length < outputFormat.getMaxDataLength())
	    outData = new byte[outputFormat.getMaxDataLength()];
	outBuffer.setData(outData);
	outBuffer.setFormat(outputFormat);

	if (dataPrev == null && hasTemporal) {
	    dataPrev = new byte[inputFormat.getMaxDataLength()];
	}

	if ((seqNo % keyFrameInterval) == 0) {
	    inFlags |= VCM.ICCOMPRESS_KEYFRAME;
	}
	
	seqNo++;
	int returnVal = VCM.icCompress(vcmHandle,
				       inFlags,
				       biOut,
				       outData,
				       biIn,
				       inBuffer.getData(),
				       ckid,
				       outFlags,
				       seqNo,
				       outputFormat.getMaxDataLength(),
				       (int) quality,
				       biIn,
				       dataPrev);

	if ((outFlags[0] & AVIF_KEYFRAME) != 0) 
	    outBufFlags |= Buffer.FLAG_KEY_FRAME;

	Object tempdata = dataPrev;
	if (hasTemporal) {
	    dataPrev = (byte[]) inBuffer.getData();
	    inBuffer.setData(tempdata);
	}
	
	if (returnVal < 0) {
	    return BUFFER_PROCESSED_FAILED;
	}
	outBuffer.setFormat(outputFormat);
	outBuffer.setSequenceNumber(seqNo);
	outBuffer.setLength(biOut.biSizeImage);
	outBufFlags = (outBuffer.getFlags() & ~Buffer.FLAG_KEY_FRAME) |
	    outBufFlags |
	    Buffer.FLAG_NO_SYNC;
	outBuffer.setFlags(outBufFlags);
	return BUFFER_PROCESSED_OK;
    }
}