public Packet decodePacket(RTMPConnection conn, RTMPDecodeState state, IoBuffer in) {
        final int position = in.position();
        //if (log.isTraceEnabled()) {
            //log.trace("decodePacket - state: {} buffer: {}", state, in);
            //log.trace("decodePacket: position {}, limit {}, {}", position, in.limit(), Hex.encodeHexString(Arrays.copyOfRange(in.array(), position, in.limit())));
            //log.trace("decodePacket: position {}, limit {}", position, in.limit());
            //int lastTs = lastTimestamp.get() != null ? lastTimestamp.get() : 0;
            //if (lastTs == 0 || lastTs >= (MEDIUM_INT_MAX - 100)) {
                //log.trace("decodePacket:{}\n{}", lastTs, Hex.encodeHexString(Arrays.copyOfRange(in.array(), position, in.limit())));
            //}
        //}
        // get RTMP state holder
        RTMP rtmp = conn.getState();
        // read the chunk header (variable from 1-3 bytes)
        final ChunkHeader chunkHeader = ChunkHeader.read(in);
        // represents "packet" header length via "format" only 1 byte in the chunk header is needed here
        int headerLength = RTMPUtils.getHeaderLength(chunkHeader.getFormat());
        headerLength += chunkHeader.getSize() - 1;
        if (in.remaining() < headerLength) {
            state.bufferDecoding(headerLength - in.remaining());
            in.position(position);
            return null;
        }
        final Header header = decodeHeader(chunkHeader, state, in, rtmp);
        // get the channel id
        final int channelId = header != null ? header.getChannelId() : chunkHeader.getChannelId();
        if (header == null || header.isEmpty()) {
            if (log.isTraceEnabled()) {
                log.trace("Header was null or empty - chh: {}", chunkHeader);
            }
            // clear / compact the input and close the channel
            in.clear();
            in.compact();
            // send a NetStream.Failed message
            StreamService.sendNetStreamStatus(conn, StatusCodes.NS_FAILED, "Bad data on channel: " + channelId, "no-name", Status.ERROR, conn.getStreamIdForChannelId(channelId));
            // close the channel on which the issue occurred, until we find a way to exclude the current data
            conn.closeChannel(channelId);
            return null;
        }
        // store the header based on its channel id
        rtmp.setLastReadHeader(channelId, header);
        // ensure that we dont exceed maximum packet size
        int size = header.getSize();
        if (size > MAX_PACKET_SIZE) {
            // Reject packets that are too big, to protect against OOM when decoding has failed in some way
            log.warn("Packet size exceeded. size={}, max={}, connId={}", header.getSize(), MAX_PACKET_SIZE, conn.getSessionId());
            // send a NetStream.Failed message
            StreamService.sendNetStreamStatus(conn, StatusCodes.NS_FAILED, "Data exceeded maximum allowed by " + (size - MAX_PACKET_SIZE) + " bytes", "no-name", Status.ERROR, conn.getStreamIdForChannelId(channelId));
            throw new ProtocolException(String.format("Packet size exceeded. size: %s", header.getSize()));
        }
        // get the size of our chunks
        int readChunkSize = rtmp.getReadChunkSize();
        // check to see if this is a new packet or continue decoding an existing one
        Packet packet = rtmp.getLastReadPacket(channelId);
        if (packet == null) {
            // create a new packet
            packet = new Packet(header.clone());
            // store the packet based on its channel id
            rtmp.setLastReadPacket(channelId, packet);
        }
        // get the packet data
        IoBuffer buf = packet.getData();
        //if (log.isTraceEnabled()) {
            //log.trace("Source buffer position: {}, limit: {}, packet-buf.position {}, packet size: {}", new Object[] { in.position(), in.limit(), buf.position(), header.getSize() });
        //}
        // read chunk
        int length = Math.min(buf.remaining(), readChunkSize);
        if (in.remaining() < length) {
            //log.debug("Chunk too small, buffering ({},{})", in.remaining(), length);
            // how much more data we need to continue?
            state.bufferDecoding(in.position() - position + length);
            // we need to move back position so header will be available during another decode round
            in.position(position);
            return null;
        }
        // get the chunk from our input
        byte[] chunk = Arrays.copyOfRange(in.array(), in.position(), in.position() + length);
        //if (log.isTraceEnabled()) {
            //log.trace("Read chunkSize: {}, length: {}, chunk: {}", readChunkSize, length, Hex.encodeHexString(chunk));
        //}
        // move the position
        in.skip(length);
        // put the chunk into the packet
        buf.put(chunk);
        if (buf.hasRemaining()) {
            //if (log.isTraceEnabled()) {
                //log.trace("Packet is incomplete ({},{})", buf.remaining(), buf.limit());
            //}
            return null;
        }
        // flip so we can read / decode the packet data into a message
        buf.flip();
        try {
            // timebase + timedelta
            final int timestamp = header.getTimer();
            // store the last ts in thread local for debugging
            //lastTimestamp.set(header.getTimerBase());
            final IRTMPEvent message = decodeMessage(conn, packet.getHeader(), buf);
            // flash will send an earlier time stamp when resetting a video stream with a new key frame. To avoid dropping it, we give it the 
            // minimal increment since the last message. To avoid relative time stamps being mis-computed, we don't reset the header we stored.
            message.setTimestamp(timestamp);
            if (log.isTraceEnabled()) {
                log.trace("Decoded message: {}", message);
            }
            packet.setMessage(message);
            if (message instanceof ChunkSize) {
                ChunkSize chunkSizeMsg = (ChunkSize) message;
                rtmp.setReadChunkSize(chunkSizeMsg.getSize());
            } else if (message instanceof Abort) {
                log.debug("Abort packet detected");
                // client is aborting a message, reset the packet because the next chunk will start a new packet
                Abort abort = (Abort) message;
                rtmp.setLastReadPacket(abort.getChannelId(), null);
                packet = null;
            }
            // collapse the time stamps on the last header after decode is complete
            Header lastHeader = rtmp.getLastReadHeader(channelId);
            lastHeader.setTimerBase(timestamp);
            // clear the delta
            //lastHeader.setTimerDelta(0);
            if (log.isTraceEnabled()) {
                log.trace("Last read header after decode: {}", lastHeader);
            }
        } finally {
            rtmp.setLastReadPacket(channelId, null);
        }
        return packet;
    }
public Packet decodePacket ( RTMPConnection conn , RTMPDecodeState state , IoBuffer in ) { final int position = in . position ( ) ; RTMP rtmp = conn . getState ( ) ; final ChunkHeader chunkHeader = ChunkHeader . read ( in ) ; int headerLength = RTMPUtils . getHeaderLength ( chunkHeader . getFormat ( ) ) ; headerLength += chunkHeader . getSize ( ) - 1 ; if ( in . remaining ( ) < headerLength ) { state . bufferDecoding ( headerLength - in . remaining ( ) ) ; in . position ( position ) ; return null ; } final Header header = decodeHeader ( chunkHeader , state , in , rtmp ) ; final int channelId = header != null ? header . getChannelId ( ) : chunkHeader . getChannelId ( ) ; if ( header == null || header . isEmpty ( ) ) { if ( log . isTraceEnabled ( ) ) { log . trace ( "Header was null or empty - chh: {}" , chunkHeader ) ; } in . clear ( ) ; in . compact ( ) ; StreamService . sendNetStreamStatus ( conn , StatusCodes . NS_FAILED , "Bad data on channel: " + channelId , "no-name" , Status . ERROR , conn . getStreamIdForChannelId ( channelId ) ) ; conn . closeChannel ( channelId ) ; return null ; } rtmp . setLastReadHeader ( channelId , header ) ; int size = header . getSize ( ) ; if ( size > MAX_PACKET_SIZE ) { log . warn ( "Packet size exceeded. size={}, max={}, connId={}" , header . getSize ( ) , MAX_PACKET_SIZE , conn . getSessionId ( ) ) ; StreamService . sendNetStreamStatus ( conn , StatusCodes . NS_FAILED , "Data exceeded maximum allowed by " + ( size - MAX_PACKET_SIZE ) + " bytes" , "no-name" , Status . ERROR , conn . getStreamIdForChannelId ( channelId ) ) ; throw new ProtocolException ( String . format ( "Packet size exceeded. size: %s" , header . getSize ( ) ) ) ; } int readChunkSize = rtmp . getReadChunkSize ( ) ; Packet packet = rtmp . getLastReadPacket ( channelId ) ; if ( packet == null ) { packet = new Packet ( header . clone ( ) ) ; rtmp . setLastReadPacket ( channelId , packet ) ; } IoBuffer buf = packet . getData ( ) ; int length = Math . min ( buf . remaining ( ) , readChunkSize ) ; if ( in . remaining ( ) < length ) { state . bufferDecoding ( in . position ( ) - position + length ) ; in . position ( position ) ; return null ; } byte [ ] chunk = Arrays . copyOfRange ( in . array ( ) , in . position ( ) , in . position ( ) + length ) ; in . skip ( length ) ; buf . put ( chunk ) ; if ( buf . hasRemaining ( ) ) { return null ; } buf . flip ( ) ; try { final int timestamp = header . getTimer ( ) ; final IRTMPEvent message = decodeMessage ( conn , packet . getHeader ( ) , buf ) ; message . setTimestamp ( timestamp ) ; if ( log . isTraceEnabled ( ) ) { log . trace ( "Decoded message: {}" , message ) ; } packet . setMessage ( message ) ; if ( message instanceof ChunkSize ) { ChunkSize chunkSizeMsg = ( ChunkSize ) message ; rtmp . setReadChunkSize ( chunkSizeMsg . getSize ( ) ) ; } else if ( message instanceof Abort ) { log . debug ( "Abort packet detected" ) ; Abort abort = ( Abort ) message ; rtmp . setLastReadPacket ( abort . getChannelId ( ) , null ) ; packet = null ; } Header lastHeader = rtmp . getLastReadHeader ( channelId ) ; lastHeader . setTimerBase ( timestamp ) ; if ( log . isTraceEnabled ( ) ) { log . trace ( "Last read header after decode: {}" , lastHeader ) ; } } finally { rtmp . setLastReadPacket ( channelId , null ) ; } return packet ; }
Decodes an IoBuffer into a Packet.

@param conn
Connection
@param state
RTMP protocol state
@param in
IoBuffer
@return Packet