FileDocCategorySizeDatePackage
NativeEncoder.javaAPI DocJMF 2.1.1e12397Mon May 12 12:21:26 BST 2003com.sun.media.codec.video.cinepakpro

NativeEncoder.java

/*
 * @(#)NativeEncoder.java	1.5 02/08/21
 *
 * Copyright (c) 1996-2002 Sun Microsystems, Inc.  All rights reserved.
 */

package com.sun.media.codec.video.cinepakpro;

import javax.media.*;
import javax.media.Format;
import javax.media.format.VideoFormat;
import javax.media.format.RGBFormat;
import javax.media.control.*;

import java.awt.Dimension;
import java.awt.Component;

import com.sun.media.*;
import com.sun.media.util.*;

public class NativeEncoder extends BasicCodec
{
    // I/O formats
	private RGBFormat inputFormat = null;
	private VideoFormat outputFormat = null;

    // Have we loaded the native library?
    private static boolean loaded = false;

    // Assume we can load it.
    private static boolean canLoad = true;

    // Pointer to native structure
    private int peer = 0;

    private boolean newframe = true;

    private int current_offset = 0;
    
    int returnVal = 0;
	
	// housekeepers from PrivateGlobals
	private	boolean		bCcontextAllocated = false;

	
     //****************************************************************
     //* Codec Methods
     //****************************************************************/

    // Initialize default formats.
    public 
NativeEncoder() 
{
	inputFormats = new RGBFormat[1];
	inputFormats[0] = new RGBFormat(
				null,			// any size
				Format.NOT_SPECIFIED,	// maxDataLen
				Format.intArray,
				Format.NOT_SPECIFIED,	// any frame rate.
				32,
				0x00FF0000,
				0x0000FF00,
				0x000000FF);

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

    protected Format getInputFormat() 
{
		
	return inputFormat;
}

    protected Format getOutputFormat() 
	{
		return outputFormat;
   }

    // Return supported output formats
    public Format [] getSupportedOutputFormats(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[ 1 ];
		out[0] = computeOutputFormat(in);
			
		return out;
    }

    private 
boolean 
verifyInputFormat( Format input ) 
{
	if (!(input instanceof RGBFormat))
	{
	    return false;
	}
			
	RGBFormat rgb = (RGBFormat) input;
	if ( //rgb.getDataType() != Format.intArray ||
	     rgb.getBitsPerPixel() != 32 ||
	     rgb.getRedMask() != 0x00FF0000 ||
	     rgb.getGreenMask() != 0x0000FF00 ||
	     rgb.getBlueMask() != 0x000000FF ||
	     rgb.getSize() == null ||
	     rgb.getLineStride() < rgb.getSize().width ||
	     rgb.getPixelStride() != 1 )
	{
	    return false;
	}

	return true;
}

    public 
Format 
setInputFormat( Format input ) 
{
	if (!verifyInputFormat(input))
	    return null;
		
	inputFormat = (RGBFormat) input;
	
	return inputFormat;
}

    public 
Format 
setOutputFormat( Format output ) 
{
	if (matches(output, outputFormats) == null)
	{
	    return null;
	}
	outputFormat = (VideoFormat) output;
	
	return outputFormat;
}

    private 
final 
VideoFormat 
computeOutputFormat( Format in ) 
{
	// Calculate the properties
	RGBFormat rgb = (RGBFormat) in;
	Dimension size = rgb.getSize();
	int maxDataLength = size.width * size.height * 3;
	VideoFormat cinepak = new VideoFormat(
								VideoFormat.CINEPAK,
					   			size, 
								maxDataLength,
					   			Format.byteArray,
								rgb.getFrameRate() );
		
	return cinepak;
}

    public void open() throws ResourceUnavailableException 
	{
		if (!canLoad)
	    	throw new ResourceUnavailableException("Unable to load" +
						   " native CinepakPro converter");

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

		if (inputFormat == null || outputFormat == null)
		    throw new ResourceUnavailableException("Formats not set " +
							   "on the cinepak encoder");
	
		if (peer != 0)
		    close();

		Dimension size = inputFormat.getSize();
				
		try 
		{
	    	peer = initCinepakEncoder(
							// source image info
						size.width, 
						size.height, 
						inputFormat.getLineStride(),
						inputFormat.getBitsPerPixel(),		// should be 24
						9,									// BGR888 packed 24 bpp 
															// TODO:  make smarter based on pixel stride, etc.
							// data rate params
						300000,								// 300 KB/s
						1000.0/inputFormat.getFrameRate(),	// 15 fps
						30,									// keyframes every 2s
						
							//job flags - don't recommend turning any other than B&W on
						false,								// fraction intercodebooks
						false,								// logging
						false,								// black and white
						
							// user knobs
						2.1,								// K/I ratio
						false,								// adapt K/I
						3500,								// nat key insertion threshold
						13									// ratio of smooths to skips is 13/8 or about 8:5
							);	
		} 
		catch (Throwable t) 
		{
		}
	
		if (peer == 0)
		{
		    throw new ResourceUnavailableException("Unable to initialize cinepak encoder");
		}
    }

    public synchronized void close() {
	if (peer != 0)
	    freeCinepakEncoder(peer);
	peer = 0;
    }

    public void reset() {
	// Anything to do?
    }

    public synchronized int process(Buffer inBuffer, Buffer outBuffer) 
	{
		Object header = null;
		
		if (isEOM(inBuffer)) 
		{
		    propagateEOM(outBuffer);
		    return BUFFER_PROCESSED_OK;
		}
			
		Format inFormat = inBuffer.getFormat();
		Format outFormat = outBuffer.getFormat();
		int [] inData = (int[]) inBuffer.getData();
		boolean flipped = ((RGBFormat) inFormat).getFlipped() == Format.TRUE;
	
		if (outputFormat.getEncoding().equals(VideoFormat.CINEPAK))
		{
		    byte [] outData = (byte[]) outBuffer.getData();
		    if (	outData == null 
				||	(outData.length < outputFormat.getMaxDataLength()) 
				)
			{
//				int	iMaxDataSize = 50000;		// make it 500k to be safe
			
				outData = new byte[outputFormat.getMaxDataLength()];
//				outData = new byte[ iMaxDataSize ];
				
				outBuffer.setData(outData);
		    }
		    
		    if (outFormat == null)
			{
				outBuffer.setFormat(outputFormat);
			}
		    
		    if (peer == 0) 
			{
				try
				{
			    	open();
				} 
				catch (ResourceUnavailableException re) 
				{
			    	return BUFFER_PROCESSED_FAILED;
				}
		    }
		    
		    Dimension size = inputFormat.getSize();
		    returnVal = encodeCinepakFrame(
							peer,
				   			inData,
							0,
						   	outData );
				   
		    if (returnVal > 0)
			{
				outBuffer.setLength(returnVal);
				outBuffer.setOffset(0);
				inBuffer.setLength(0);
				outBuffer.setTimeStamp(inBuffer.getTimeStamp());

				if ( wasKeyFrame( peer ) ) {
				    outBuffer.setFlags( Buffer.FLAG_KEY_FRAME );
				} else {
				    outBuffer.setFlags( outBuffer.getFlags() &
							~Buffer.FLAG_KEY_FRAME);
				}

				return BUFFER_PROCESSED_OK;
			}
			
		    outBuffer.setDiscard(true);
		    return BUFFER_PROCESSED_FAILED;
		}// if outputformat is CINEPAK
	
		return BUFFER_PROCESSED_FAILED;	
	}// end of process()

    public void finalize() {
	close();
    }

    public String getName() 
	{
		return "CinepakPro Encoder by CTi";
    }

    //****************************************************************
    // * Native Methods
    //****************************************************************/

    // Initializes the native encoder
	//
	//	returns 
	//		a positive encoder instance handle which is needed for every subsequent call
	//			or
	//		a negative error code
	//		
	//		0 == some kinda really gross error...
	//
    private native int initCinepakEncoder(
								// source image info
							int 	width_pixels, 		// width of source image in pixels
							int 	height_pixels, 		// height of source image in pixels
							int		rowPitch_bytes,		// pitch of rows in bytes
							int		colorDepth_bpp,		// color depth in bits per pixel
							int		dibType,			// code for color organization
							
								// parameters
									// data rate controls
							int		dataRate_kBps,		// average data rate in kilobytes/s
							double	frameDuration_ms,	// frame duration in milliseconds.
														// 	if changing frame rates, re-init
							int		forceKeyFrameEvery_f,
														// forces key frames to be inserted every x frames
							
									// job flags
							boolean	doFractionInterCodeBook,	// true to turn on fractional refining of inter codebooks
							boolean	doLogRequested,				// true to enable logging
							boolean	doBlackAndWhite,			// true to enable B&W only encoding
									
									// user controls
							double	initKIRatio,		// starting ratio of sizes of Key to Interframes
														//	permanent ratio if doUseAdaptiveKIRatio is false
							boolean	doUseAdaptiveKIRatio, //or starting point if true
							int		NatKeyThreshold,	// amount of change required to force a natural keyframe
							int		SpatialTemporalHint	// hint value to influence better spatial resolution or
														// 	faster codebook changes
								);
		
    
    //*
    // * Encodes the RGB data and returns the output length (positive)
     //* Returns zero if it couldn't encode, or a negative value to indicate
     //* the error.
     //
	 //		note that key/interframe information is lost!
	 //
	 //		so call wasKeyFrame() immed after to find out if it was a keyframe
	 //
    private native int encodeCinepakFrame(
								int		instance,			// returned from initCinepakEncoder() 
								int [] 	inData, 			// source pixels in format described to init()
								int 	dataStart,			// offset in inData where the image really starts
				  				byte [] outData				// destination of output data -- compressed frame
									);
	private native boolean wasKeyFrame( int instance );


    // Frees any native structures
	//	
	//	returns error code
	//
	//		0 == success, noErr
	//
    private native int freeCinepakEncoder(int instance);

    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;
    }
    
    /****************************************************************
     * Inner Classes
     ****************************************************************/
    
    class DC implements FrameProcessingControl, QualityControl  {

		public
		int
		getFramesDropped( )
		{
			return 0;
		}

	public Component getControlComponent() 
	{
	    return null;
	}
	
	public boolean setMinimalProcessing(boolean on) {
	    //dropNextFrame(true);
	    return false;
	}
	
	/**
	* 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);
	   */
	}

	/**
	* 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;
	}
}		
}