FileDocCategorySizeDatePackage
NativeDecoder.javaAPI DocJMF 2.1.1e13526Mon May 12 12:20:46 BST 2003com.sun.media.codec.video.vh263

NativeDecoder

public class NativeDecoder extends VideoCodec

Fields Summary
private int
nativeData
pointer to the native structure of H263 decoder
private int
pictureDesc
pointer to the native structure of the picture descriptor
private int
PBFrameCap
private int
bsStart
private int
nextGOB
private int
pendingFrame
private static int
MAX_SEQ
private long
prevSeq
private static final boolean
debug
static final int[]
widths
static final int[]
heights
private int
videoWidth
private int
videoHeight
private boolean
FormatSizeInitFlag
private int
payloadLength
Constructors Summary
public NativeDecoder()




      
	supportedInputFormats = new VideoFormat[] {
				new VideoFormat(VideoFormat.H263),
				new VideoFormat(VideoFormat.H263_RTP),
				new VideoFormat(VideoFormat.H263_1998_RTP)
	};
        defaultOutputFormats  = new VideoFormat[] {new YUVFormat() };
        PLUGIN_NAME = "H.263 Decoder";

    
Methods Summary
public booleancheckFormat(javax.media.Format format)


      if ( (format.getEncoding()).equals(VideoFormat.H263_RTP) ) {
        return true;
      }
      else if ( (format.getEncoding()).equals(VideoFormat.H263_1998_RTP) ) {
        return true;
      }
      else {
        return super.checkFormat(format);
      }

    
public voidclose()

        closeNativeDecoder();
	super.close();
    
private native booleancloseNativeDecoder()

private native booleandecodeFrameNative(javax.media.Buffer in, javax.media.Buffer out)

private native booleandecodePacketNative(byte[] in, int inputOffset, int inputLength, byte[] out, byte[] payloadHeader, int payloadOffset, int sync, int h263_1998)

public static intget1998PayloadHeaderLength(byte[] input, int offset)


       int l = 2 + ((input[offset]&0x01) << 5) | ((input[offset+1]&0xf8) >> 3);

	if ( (input[offset] & 0x02) != 0) { // Video Redundancy present
	    l++;
	}

	return l;
    
protected javax.media.Format[]getMatchingOutputFormats(javax.media.Format in)

      	VideoFormat     ivf  = (VideoFormat) in;
	Dimension       inSize = ivf.getSize();
        int             inMaxDataLength=ivf.getMaxDataLength();
        int             outNumOfPixels;


        if ( ( (ivf.getEncoding()).equals(VideoFormat.H263_RTP) )
        	|| ( (ivf.getEncoding()).equals(VideoFormat.H263_1998_RTP) ) ) {

	    if (inSize == null)
		outNumOfPixels = videoWidth * videoHeight;
	    else {
		outNumOfPixels = inSize.width * inSize.height;
		videoWidth = inSize.width;
		videoHeight = inSize.height;
	    }
      
          outNumOfPixels=videoWidth*videoHeight;
          supportedOutputFormats= new  VideoFormat[] {

                new YUVFormat (
                new Dimension(videoWidth,videoHeight),
                (outNumOfPixels)+ ((outNumOfPixels) >> 1),
                Format.byteArray,
                ivf.getFrameRate(),			       
                YUVFormat.YUV_420 ,
                videoWidth,
                videoWidth>>1,
                0,
                outNumOfPixels,
                outNumOfPixels+(outNumOfPixels >>2)
                )};

        }
        else {

         Dimension  outSize=movieSizeTo263Size(inSize);
         outNumOfPixels=outSize.width*outSize.height;
         videoWidth=outSize.width;
         videoHeight=outSize.height;
         supportedOutputFormats= new  VideoFormat[] {
                new YUVFormat (
                inSize,
                (outNumOfPixels)+ ((outNumOfPixels) >> 1),
                Format.byteArray,
                ivf.getFrameRate(),			       
                YUVFormat.YUV_420 ,
                outSize.width,
                outSize.width>>1,
                0,
                outNumOfPixels,
                outNumOfPixels+(outNumOfPixels >>2)
                ) };

        }

        return  supportedOutputFormats;
    
private intgetPayloadHeaderLength(byte[] input, int offset)


       int l = 0;
       byte b = input[offset];

	if ( (b & 0x80) != 0) { //mode B or C
	    if ((b & 0x40) != 0) //mode C
		l = 12;
	    else //mode B
		l = 8;
	} else { //mode A
	    l = 4;
	}

	return l;
    
private intgetSequenceDiff(long p, long c)

	if (c > p)
	    return (int) (c - p);
	if (c == p)
	    return 0;
	if (p > MAX_SEQ - 100 && c < 100) {
	    // Allow for the case where sequence number has wrapped.
	    return (int) ((MAX_SEQ - p) + c + 1);
	}
	return (int) (c - p);
    
protected voidinitDecoder()

        closeNativeDecoder();		     // close the decoder if it was openned
        initNativeDecoder(videoWidth,videoHeight);
    
private native booleaninitNativeDecoder(int width, int height)

private static native booleaninitNativeDecoderClass()

private java.awt.DimensionmovieSizeTo263Size(java.awt.Dimension movieSize)

       int width  = (movieSize.width  + 0xf) & 0xfffffff0;
       int height = (movieSize.height + 0xf) & 0xfffffff0;
       int formatCap = (width * height) >> 8;

       /* transform the number of macroblocks into format*/
       if (formatCap <= 48)
           return new Dimension(128,96);    // SQCIF
       if (formatCap <= 99)
           return new Dimension(176,144);   // QCIF
       if (formatCap <= 396)
           return new Dimension(352,288);   // CIF
       if (formatCap <= 1584)
           return new Dimension(704,576);   // 4CIF
       if (formatCap <= 6336)
           return new Dimension(1408,1152); // 16CIF

       return new Dimension(0,0);    // ERROR


    
public voidopen()

        try {
	    JMFSecurityManager.loadLibrary("jmutil");
            JMFSecurityManager.loadLibrary("jmvh263");
            initNativeDecoderClass();
            initDecoder();
	    super.open();
            return;
        } catch (Throwable e) {
	    //System.out.println(e);
        }

        throw new ResourceUnavailableException("could not load jmvh263");
    
public intprocess(javax.media.Buffer inputBuffer, javax.media.Buffer outputBuffer)



      boolean rtpData = false;
      boolean rtp1998Data = false;
      boolean ret=false;
      RTPHeader rtpHeader=null;
      int newWidth=videoWidth;
      int newHeight=videoHeight;

      if (!checkInputBuffer(inputBuffer) ) {
         return BUFFER_PROCESSED_FAILED;
      }

      if (isEOM(inputBuffer) ) {
         propagateEOM(outputBuffer);
         return BUFFER_PROCESSED_OK;
      }

      VideoFormat ivf=(VideoFormat) inputBuffer.getFormat();
      int inLength=inputBuffer.getLength();
      int inMaxLength=ivf.getMaxDataLength();
      int outMaxLength=outputFormat.getMaxDataLength();
      int inputOffset=inputBuffer.getOffset();

      byte[] inData =(byte[]) inputBuffer.getData();

      if ( (ivf.getEncoding()).equals(VideoFormat.H263_RTP) ) {
        rtpData = true;
        payloadLength=getPayloadHeaderLength(inData,inputOffset);
        if ( (inData[inputOffset+payloadLength] == 0) && (inData[inputOffset+payloadLength+1] == 0) && ((inData[inputOffset+payloadLength+2] & 0xfc) == 0x80)) {
              int s = (inData[inputOffset+payloadLength+4] >> 2) & 0x7;

	      newWidth=widths[s];
	      newHeight=heights[s];
	      FormatSizeInitFlag=true;

	}
      }
      else if ( (ivf.getEncoding()).equals(VideoFormat.H263_1998_RTP) ) {
        rtp1998Data = true;
	int s = -1;
	int picOffset = -1;
	if (getSequenceDiff(prevSeq, inputBuffer.getSequenceNumber()) != 1) {
	    boolean discard = ((inData[inputOffset] & 0x04) == 0);
	    // Not part of the continuation, report lost packet
	    if (debug) {
		System.err.println(
			"NativeDecoder: Sequence out of order, expecting "
					+ (prevSeq+1) + " got "
					+ inputBuffer.getSequenceNumber()
					+ (discard ? " discard packet" : ""));
	    }
	    if (discard)
		return OUTPUT_BUFFER_NOT_FILLED;
	}
	prevSeq = inputBuffer.getSequenceNumber();
        payloadLength=get1998PayloadHeaderLength(inData,inputOffset);
	if (payloadLength > 5) {
	    // Use PIC header in payload header
	    if ( ((inData[inputOffset] & 0x02) == 0x02)
		&& ((inData[inputOffset+3] & 0xfc) == 0x80)) {
		picOffset = inputOffset + 3;
	    } else if ((inData[inputOffset+2] & 0xfc) == 0x80) {
		picOffset = inputOffset + 2;
	    }
	} else if ( ((inData[inputOffset] & 0x04) == 0x04)
		&& ((inData[inputOffset+payloadLength] & 0xfc) == 0x80)) {
	    picOffset = inputOffset + payloadLength;
	}

	if (picOffset >= 0) {
	    s = (inData[picOffset+2] >> 2) & 0x7;
	    if (s == 7) {
		// Extended PTYPE, picture size is in the extension
		// if UFEP = 001
		if (((inData[picOffset+3] >> 1) & 0x07) == 1) {
		    s = ((inData[picOffset+3] << 2) & 0x04) |
				((inData[picOffset+4] >> 6) & 0x03);
		} else {
		    s = -1;	// picture type not present
		}
	    }
	}

	if (s >= 0) {

              newWidth=widths[s];
	      newHeight=heights[s];
	      FormatSizeInitFlag=true;

	}
      }
      if ( (videoWidth!=newWidth) || (videoHeight!=newHeight)  ) {

        videoWidth=newWidth;
        videoHeight=newHeight;


        int outNumOfPixels=videoWidth*videoHeight;

        outputFormat = new YUVFormat (
		new Dimension(videoWidth,videoHeight),
		(outNumOfPixels)+ ((outNumOfPixels) >> 1),
		Format.byteArray,
		ivf.getFrameRate(),			       
		YUVFormat.YUV_420 ,
		videoWidth,
		videoWidth>>1,
		0,
		outNumOfPixels,
		outNumOfPixels+(outNumOfPixels >>2)
        );


        outMaxLength = outputFormat.getMaxDataLength();
        // outMaxLength = videoWidth * videoHeight;
        videoResized();

	FormatSizeInitFlag=true;

      }

      if ( (rtpData || rtp1998Data) && !FormatSizeInitFlag) {
        return BUFFER_PROCESSED_FAILED;
      }

      byte[] outData = validateByteArraySize(outputBuffer,outMaxLength );

      /*
       *  <PATCH> check for insufficient input:
       *  The decoder might read up to 8 additional bytes before checking for EOS
       *  It does not bother the native code, but can cause ArrayOutOfBounds
       *  in Java code
       */
      if ( (inLength+8+inputOffset)>inData.length) {
         //System.out.println("allocating more data for H.263");
         int newLength=(inLength > inMaxLength) ? inLength : inMaxLength;

         byte[] tempArray=new byte[inputOffset+newLength+8];
         System.arraycopy(inData,0,tempArray,0,inLength+inputOffset);
         inData=tempArray;
         inputBuffer.setData(tempArray);
         //inputBuffer.setOffset(0);

      }

      /*
       *  <PATCH> pad input with EOS
       */
      inData[inputOffset+inLength] = 0;
      inData[inputOffset+inLength+1] = 0;
      inData[inputOffset+inLength+2]= (byte) 0xfc;
      inLength += 3;
      inputBuffer.setLength(inLength);

      if (rtpData) {
        inLength-=payloadLength;  // this is the length of the bitstream
        //rtpHeader= (RTPHeader) inputBuffer.getHeader();
	int marker = 0;
	if ((inputBuffer.getFlags() & Buffer.FLAG_RTP_MARKER) != 0)
	  marker = 1;
        ret = decodePacketNative(inData,inputOffset+payloadLength,inLength,outData,inData,inputOffset,marker,0);
      } else
      if (rtp1998Data) {
        inLength-=payloadLength;  // this is the length of the bitstream
        //rtpHeader= (RTPHeader) inputBuffer.getHeader();
	int marker = 0;
	if ((inputBuffer.getFlags() & Buffer.FLAG_RTP_MARKER) != 0) {
	  marker = 1;
	} else {
	    inLength -= 3;	// skip the EOS pad
	}
        ret = decodePacketNative(inData,inputOffset+payloadLength,inLength,outData,inData,inputOffset,marker,1);
      }
      else {
        ret = decodeFrameNative(inputBuffer, outputBuffer);
      }

      if (ret) {
         updateOutput(outputBuffer,outputFormat, outMaxLength, 0);
	 outputBuffer.setFormat(outputFormat);
         return BUFFER_PROCESSED_OK;
       }
       else {
         return OUTPUT_BUFFER_NOT_FILLED;
       }


    
public voidreset()

      //we do not initialize the decoder as the first frame is key frame
    
public javax.media.FormatsetInputFormat(javax.media.Format input)

	Format ret = super.setInputFormat(input);
	if (ret == null)
	    return null;
	if (opened) {
	    close();
	    try {
		setOutputFormat(getMatchingOutputFormats(input)[0]);
		open();
	    } catch (Exception e) {
		return null;
	    }
	}
	return ret;
    
protected voidvideoResized()

        initDecoder();