/* * @(#)Packetizer.java 1.22 00/10/05 * * Copyright 1998-99 by Sun Microsystems, Inc., * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A. * All rights reserved. * * This software is the confidential and proprietary information * of Sun Microsystems, Inc. ("Confidential Information"). You * shall not disclose such Confidential Information and shall use * it only in accordance with the terms of the license agreement * you entered into with Sun. */ package com.sun.media.codec.video.jpeg; import javax.media.*; import javax.media.Format; import javax.media.format.VideoFormat; import javax.media.control.*; import java.awt.Dimension; import java.awt.Component; import com.sun.media.*; import com.sun.media.util.*; import javax.media.format.JPEGFormat; //import javax.media.rtp.RTPHeader; public class Packetizer extends BasicCodec { static final JPEGFormat fJPEG = new JPEGFormat(); // I/O formats private VideoFormat inputFormat = null; private VideoFormat outputFormat = null; // default packet size of JPEG payload for RTP. can be changed // using encoding controls ? private int PACKET_SIZE = 960; // current sequence number on RTP format packets. private int currentSeq = 0; // current timestamp on RTP format packets. //private long timestamp = (long) (System.currentTimeMillis() * Math.random()); // next 4 varaibles used for RTP packetization private int copyLength = PACKET_SIZE; private boolean newFrame = true; private boolean dropFrame = false; private boolean minimal = false; private int offset = 0; private int frameLength = 0; private static final int J_SOF = 0xc0; private static final int J_SOF1 = 0xc1; private int decimation = -1; // default frame rate for RTP JPEG format. Should normally be // specified when frame rate is set. private static final int DEFAULT_FRAMERATE = 15; private float frame_duration = -1; /**************************************************************** * Codec Methods ****************************************************************/ // Initialize default formats. public Packetizer() { inputFormats = new VideoFormat[] {new VideoFormat(VideoFormat.JPEG)}; outputFormats = new VideoFormat[] {new VideoFormat(VideoFormat.JPEG_RTP)}; 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[1]; controls[0] = fpc; } 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 JPEG video format if (!verifyInputFormat(in)) return new Format[0]; Format out [] = new Format[1]; // check the frame Rate and if it is dont care, its needs // to be set to the default frame rate. if ( ((VideoFormat)in).getFrameRate() == VideoFormat.NOT_SPECIFIED) out[0] = new VideoFormat(VideoFormat.JPEG_RTP, ((VideoFormat)in).getSize(), ((VideoFormat)in).getMaxDataLength(), Format.byteArray, DEFAULT_FRAMERATE); else out[0] = new VideoFormat(VideoFormat.JPEG_RTP, ((VideoFormat)in).getSize(), ((VideoFormat)in).getMaxDataLength(), Format.byteArray, ((VideoFormat)in).getFrameRate()); return out; } private boolean verifyInputFormat(Format input) { // -- by hsy to improve robustness & performance //if ((input instanceof VideoFormat) && // (input.getEncoding().equals(VideoFormat.JPEG))) // return true; //return false; return fJPEG.matches(input); } public Format setInputFormat(Format input) { if (!verifyInputFormat(input)) return null; inputFormat = (VideoFormat)input; float rate = inputFormat.getFrameRate(); if (rate != Format.NOT_SPECIFIED) // frame duration in msec frame_duration = 1000/rate; if (opened) { outputFormat = (VideoFormat) getSupportedOutputFormats(input)[0]; } return input; } public Format setOutputFormat(Format output) { if (matches(output, outputFormats) == null) return null; outputFormat = (VideoFormat) output; return output; } public void open() throws ResourceUnavailableException { if (inputFormat == null || outputFormat == null) throw new ResourceUnavailableException("Incorrect formats set on JPEG Packetizer"); // Validate the sizes. Dimension size = inputFormat.getSize(); if (size != null){ // the JPEG packetizer cannot handle non standard format sizes, // so it checks the size here and will return null if the size // is not a multiple of 8 pixels if ((size.width % 8 != 0) || (size.height % 8 != 0)){ Log.error("Class: " + this); Log.error(" can only packetize in sizes of multiple of 8 pixels."); throw new ResourceUnavailableException("Incorrect formats set on JPEG Packetizer"); } } super.open(); } public void reset() { // Anything to do? } public synchronized int process(Buffer inBuffer, Buffer outBuffer) { if (isEOM(inBuffer)) { propagateEOM(outBuffer); return BUFFER_PROCESSED_OK; } if (inBuffer.isDiscard()) { updateOutput(outBuffer, outputFormat, 0, 0); outBuffer.setDiscard(true); return OUTPUT_BUFFER_NOT_FILLED; } if (inBuffer.getLength() <= 0) { outBuffer.setDiscard(true); return OUTPUT_BUFFER_NOT_FILLED; } byte [] inData = (byte[]) inBuffer.getData(); outBuffer.setFormat(outputFormat); Dimension size = inputFormat.getSize(); int keyFrame = 0; if (newFrame) { if (dropFrame || minimal) { outBuffer.setDiscard(true); return BUFFER_PROCESSED_OK; } int tempdec = peekJPEGDecimation(inData, inBuffer.getLength()); if (tempdec >= 0) decimation = tempdec; if (inputFormat instanceof JPEGFormat) stripTables(inBuffer); frameLength = inBuffer.getLength(); offset = 0; newFrame = false; keyFrame = Buffer.FLAG_KEY_FRAME; } //copy correct part of the encoded buffer into outputbuffer if (frameLength - offset < PACKET_SIZE) copyLength = frameLength - offset; else copyLength = PACKET_SIZE; byte[] outData = (byte[])outBuffer.getData(); if ((outData == null) || outData.length < copyLength + 8){ outData = new byte[copyLength + 8]; outBuffer.setData(outData); } System.arraycopy(inData, offset + inBuffer.getOffset(), outData, 8, copyLength); int qfactor = (inputFormat instanceof JPEGFormat ? ((JPEGFormat)inputFormat).getQFactor() : 80); decimation = (inputFormat instanceof JPEGFormat ? ((JPEGFormat)inputFormat).getDecimation() : decimation); if (decimation == -1) decimation = 1; outBuffer.setLength(copyLength + 8); outBuffer.setOffset(0); //outBuffer.setTimeStamp(timestamp); outBuffer.setSequenceNumber(currentSeq++); outBuffer.setFormat(outputFormat); outData[0] = 0; outData[1] = (byte) (offset >> 16); outData[2] = (byte) (offset >> 8); outData[3] = (byte) (offset); outData[4] = (byte)decimation; outData[5] = (byte)qfactor; outData[6] = (byte)(size.width/8); outData[7] = (byte)(size.height/8); offset += copyLength; outBuffer.setFlags(outBuffer.getFlags() | keyFrame); if (offset == frameLength) { outBuffer.setFlags(outBuffer.getFlags() | Buffer.FLAG_RTP_MARKER); newFrame = true; return BUFFER_PROCESSED_OK; } return INPUT_BUFFER_NOT_CONSUMED; }// end of process() public void finalize() { close(); } public String getName() { return "JPEG Packetizer"; } int peekJPEGDecimation(byte [] data, int dataLen) { int i = 0; int code; if ((data[0] & 0xFF) != 0xFF || data[1] == 0) return -1; while (i < dataLen - 2) { if ((data[i] & 0xFF) == 0xFF) { i++; code = data[i] & 0xFF; i++; switch (code) { case J_SOF: case J_SOF1: return getDecimationFromSOF(data, i, dataLen); } } else i++; } return -1; } private void stripTables(Buffer inb) { byte [] data = (byte[]) inb.getData(); int offset = inb.getOffset(); int length = inb.getLength(); int i = offset; while (i < length + offset - 8) { if (data[i] == (byte)0xFF) { if (data[i+1] == (byte)0xDA) { // Found SOS Start of Scan marker // Skip over this block int blockSize = ((data[i+2] & 0xFF) << 8) | (data[i+3] & 0xFF); i += 2 + blockSize; System.arraycopy(data, i, data, 0, length + offset - i); inb.setOffset(0); inb.setLength(length + offset - i); break; } } i++; } } int getDecimationFromSOF(byte [] data, int i, int length) { int sectionLen; int dummy; int ncomp; int deccode; int hsf, vsf; int id; sectionLen = (data[i++] & 0xFF) << 8; sectionLen |= (data[i++] & 0xFF); i += 5; // skip precision, 2height, 2width ncomp = (data[i++] & 0xFF); if (sectionLen != ncomp * 3 + 8) System.err.println("Bogus SOF length"); id = data[i++] & 0xFF; deccode = data[i++] & 0xFF; hsf = (deccode >> 4) & 15; vsf = (deccode ) & 15; if (vsf == 2 && hsf == 2) return JPEGFormat.DEC_420; else if (vsf == 1 && hsf == 1) return JPEGFormat.DEC_444; else return JPEGFormat.DEC_422; } }