diff -r 9bdff6cbd120 -r 2bf440c54ca5 src/jlibrtp/PktBuffer.java --- a/src/jlibrtp/PktBuffer.java Thu May 28 14:26:06 2009 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,473 +0,0 @@ -/** - * Java RTP Library (jlibrtp) - * Copyright (C) 2006 Arne Kepp - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ -package jlibrtp; - -/** - * A PktBuffer stores packets either for buffering purposes, - * or because they need to be assimilated to create a complete frame. - * - * This behavior can be controlled through rtpSession.pktBufBehavior() - * - * It optionally drops duplicate packets. - * - * Note that newest is the most recently received, i.e. highest timeStamp - * Next means new to old (from recently received to previously received) - * - * @author Arne Kepp - */ -public class PktBuffer { - /** The RTPSession holds information common to all packetBuffers, such as max size */ - RTPSession rtpSession; - /** SSRC of the the participant that this buffer is for */ - long SSRC; - /** The parent participant */ - Participant p; - /** The length of the buffer */ - int length = 0; - /** The oldest, least recently received, packet */ - PktBufNode oldest = null; - /** The newest, most recently received, packet */ - PktBufNode newest = null; - - /** The last sequence number received */ - int lastSeqNumber = -1; - /** The last timestamp */ - long lastTimestamp = -1; - - /** - * Creates a new PktBuffer, a linked list of PktBufNode - * - * @param rtpSession the parent RTPSession - * @param p the participant to which this packetbuffer belongs. - * @param aPkt The first RTP packet, to be added to the buffer - */ - protected PktBuffer(RTPSession rtpSession, Participant p, RtpPkt aPkt) { - this.rtpSession = rtpSession; - this.p = p; - SSRC = aPkt.getSsrc(); - PktBufNode newNode = new PktBufNode(aPkt); - oldest = newNode; - newest = newNode; - //lastSeqNumber = (aPkt.getSeqNumber() - 1); - //lastTimestamp = aPkt.getTimeStamp(); - length = 1; - } - - /** - * Adds a packet, this happens in constant time if they arrive in order. - * Optimized for the case where each pkt is a complete frame. - * - * @param aPkt the packet to be added to the buffer. - * @return integer, negative if operation failed (see code) - */ - protected synchronized int addPkt(RtpPkt aPkt) { - if(aPkt == null) { - System.out.println("! PktBuffer.addPkt(aPkt) aPkt was null"); - return -5; - } - - long timeStamp = aPkt.getTimeStamp(); - if(RTPSession.rtpDebugLevel > 7) { - System.out.println("-> PktBuffer.addPkt() , length:" + length + " , timeStamp of Pkt: " + Long.toString(timeStamp)); - } - - - PktBufNode newNode = new PktBufNode(aPkt); - if(aPkt.getSsrc() != SSRC) { - System.out.println("PktBuffer.addPkt() SSRCs don't match!"); - } - - int retVal = 0; - if(this.rtpSession.pktBufBehavior > 0) { - retVal = bufferedAddPkt(newNode); - } else if(this.rtpSession.pktBufBehavior == 0) { - retVal = filteredAddPkt(newNode); - } else if(this.rtpSession.pktBufBehavior == -1) { - retVal = unfilteredAddPkt(newNode); - } - - - if(RTPSession.rtpDebugLevel > 7) { - if(RTPSession.rtpDebugLevel > 10) { - this.debugPrint(); - } - System.out.println("<- PktBuffer.addPkt() , length:" + length + " returning " + retVal); - } - return retVal; - } - - /** - * Adds packets in the same order that they arrive, - * doesn't do any filering or processing. - * - * @param newNode the node to add to the packet buffer - * @return 0 if everything is okay, -1 otherwise - */ - private int unfilteredAddPkt(PktBufNode newNode) { - if(RTPSession.rtpDebugLevel > 8) { - System.out.println("<-> PktBuffer.unfilteredAddPkt()"); - } - //No magic, just add to the end - if(oldest != null) { - oldest.nextFrameQueueNode = newNode; - newNode.prevFrameQueueNode = oldest; - oldest = newNode; - } else { - oldest = newNode; - newest = newNode; - } - return 0; - } - - /** - * Takes care of duplicate packets - * - * @param newNode the node to add to the packet buffer - * @return 0 if everything is okay, -1 otherwise - */ - private int filteredAddPkt(PktBufNode newNode) { - if(RTPSession.rtpDebugLevel > 8) { - System.out.println("<-> PktBuffer.filteredAddPkt()"); - } - - if(length == 0) { - // The buffer was empty, this packet is the one and only. - newest = newNode; - oldest = newNode; - length = 1; - } else { - // The packetbuffer is not empty. - if(newNode.timeStamp > newest.timeStamp || newNode.seqNum > newest.seqNum && (newNode.seqNum - newest.seqNum) < 10) { - // Packet came in order - newNode.nextFrameQueueNode = newest; - newest.prevFrameQueueNode = newNode; - newest = newNode; - length++; - } else { - if(RTPSession.rtpDebugLevel > 2) { - System.out.println("PktBuffer.filteredAddPkt Dropped a packet due to lag! " + newNode.timeStamp + " " - + newNode.seqNum + " vs "+ oldest.timeStamp + " " + oldest.seqNum); - } - return -1; - } - } - - return 0; - } - - /** - * Does most of the packet organization for the application. - * Packets are put in order, duplicate packets or late arrivals are discarded - * - * If multiple packets make up a frame, these will also be organized - * by RTP timestamp and sequence number, and returned as a complete frame. - * - * @param newNode the node to add to the packet buffer - * @return 0 if everything is okay, -1 otherwise - */ - private int bufferedAddPkt(PktBufNode newNode) { - if(RTPSession.rtpDebugLevel > 8) { - System.out.println("<-> PktBuffer.bufferedAddPkt()"); - } - if(length == 0) { - // The buffer was empty, this packet is the one and only. - newest = newNode; - oldest = newNode; - } else { - // The packetbuffer is not empty. - if(newNode.timeStamp > newest.timeStamp || newNode.seqNum > newest.seqNum) { - // Packet came in order - newNode.nextFrameQueueNode = newest; - newest.prevFrameQueueNode = newNode; - newest = newNode; - } else { - //There are packets, we need to order this one right. - if(! pktOnTime(newNode.timeStamp, newNode.seqNum) && rtpSession.pktBufBehavior > -1) { - // We got this too late, can't put it in order anymore. - if(RTPSession.rtpDebugLevel > 2) { - System.out.println("PktBuffer.addPkt Dropped a packet due to lag! " + newNode.timeStamp + " " - + newNode.seqNum + " vs "+ oldest.timeStamp + " " + oldest.seqNum); - } - return -1; - } - - //Need to do some real work, find out where it belongs (linear search from the back). - PktBufNode tmpNode = newest; - while(tmpNode.timeStamp > newNode.timeStamp) { - tmpNode = tmpNode.nextFrameQueueNode; - } - - if( tmpNode.timeStamp == newNode.timeStamp - && rtpSession.frameReconstruction - && newNode.seqNum != tmpNode.seqNum) { - //Packet has same timestamp, presumably belongs to frame. Need to order within frame. - if(RTPSession.rtpDebugLevel > 8) { - System.out.println("Found pkt with existing timeStamp: " + newNode.timeStamp); - } - int ret = addToFrame(tmpNode, newNode); - if(ret != 0) { - return ret; - } - } else { - - // Check that it's not a duplicate - if(tmpNode.timeStamp == newNode.timeStamp && newNode.seqNum == tmpNode.seqNum) { - if(RTPSession.rtpDebugLevel > 2) { - System.out.println("PktBuffer.addPkt Dropped a duplicate packet! " - + newNode.timeStamp + " " + newNode.seqNum ); - } - return -1; - } - - // Insert into buffer - newNode.nextFrameQueueNode = tmpNode; - newNode.prevFrameQueueNode = tmpNode.prevFrameQueueNode; - - // Update the node behind - if(newNode.prevFrameQueueNode != null) { - newNode.prevFrameQueueNode.nextFrameQueueNode = newNode; - } - tmpNode.prevFrameQueueNode = newNode; - - if(newNode.timeStamp > newest.timeStamp) { - newest = newNode; - } - } - } - } - // Update the length of this buffer - length++; - return 0; - } - - /** - * - * @param frameNode the node currently representing the frame in the packet buffer - * @param newNode the new node to be added to the frame - * @return 0 if no error, -2 if this is a duplicate packet - */ - private int addToFrame(PktBufNode frameNode, PktBufNode newNode) { - // Node has same timeStamp, assume pkt belongs to frame - - if(frameNode.seqNum < newNode.seqNum) { - // this is not the first packet in the frame - frameNode.pktCount++; - - // Find the right spot - while( frameNode.nextFrameNode != null - && frameNode.nextFrameNode.seqNum < newNode.seqNum) { - frameNode = frameNode.nextFrameNode; - } - - // Check whether packet is duplicate - if(frameNode.nextFrameNode != null - && frameNode.nextFrameNode.seqNum == newNode.seqNum) { - if(RTPSession.rtpDebugLevel > 2) { - System.out.println("PktBuffer.addPkt Dropped a duplicate packet!"); - } - return -2; - } - - newNode.nextFrameNode = frameNode.nextFrameNode; - frameNode.nextFrameNode = newNode; - - } else { - // newNode has the lowest sequence number - newNode.nextFrameNode = frameNode; - newNode.pktCount = frameNode.pktCount + 1; - - //Update the queue - if(frameNode.nextFrameQueueNode != null) { - frameNode.nextFrameQueueNode.prevFrameQueueNode = newNode; - newNode.nextFrameQueueNode = frameNode.nextFrameQueueNode; - frameNode.nextFrameQueueNode = null; - } - if(frameNode.prevFrameQueueNode != null) { - frameNode.prevFrameQueueNode.nextFrameQueueNode = newNode; - newNode.prevFrameQueueNode = frameNode.prevFrameQueueNode; - frameNode.prevFrameQueueNode = null; - } - if(newest.timeStamp == newNode.timeStamp) { - newest = newNode; - } - } - - return 0; - } - - /** - * Checks the oldest frame, if there is one, sees whether it is complete. - * @return Returns null if there are no complete frames available. - */ - protected synchronized DataFrame popOldestFrame() { - if(RTPSession.rtpDebugLevel > 7) { - System.out.println("-> PktBuffer.popOldestFrame()"); - } - if(RTPSession.rtpDebugLevel > 10) { - this.debugPrint(); - } - - if(this.rtpSession.pktBufBehavior > 0) { - return this.bufferedPopFrame(); - } else { - return this.unbufferedPopFrame(); - } - } - - /** - * Will return the oldest frame without checking whether it is in - * the right order, or whether we should wate for late arrivals. - * - * @return the first frame on the queue, null otherwise - */ - private DataFrame unbufferedPopFrame() { - if(oldest != null) { - PktBufNode retNode = oldest; - - popFrameQueueCleanup(retNode, retNode.seqNum); - - return new DataFrame(retNode, this.p, - rtpSession.appIntf.frameSize(retNode.pkt.getPayloadType())); - } else { - return null; - } - } - - /** - * Only returns if the buffer is full, i.e. length exceeds - * rtpSession.pktBufBehavior, or if the next packet directly - * follows the previous one returned to the application. - * - * @return first frame in order, null otherwise - */ - private DataFrame bufferedPopFrame() { - PktBufNode retNode = oldest; - /** - * Three scenarios: - * 1) There are no packets available - * 2) The first packet is vailable and in order - * 3) The first packet is not the next on in the sequence - * a) We have exceeded the wait buffer - * b) We wait - */ - //System.out.println(" Debug:" +(retNode != null) + " " + (retNode.seqNum == this.lastSeqNumber + 1) - // + " " + ( retNode.seqNum == 0 ) + " " + (this.length > this.rtpSession.maxReorderBuffer) - // + " " + (this.lastSeqNumber < 0)); - - // Pop it off, null all references. - if( retNode != null && (retNode.seqNum == this.lastSeqNumber + 1 || retNode.seqNum == 0 - || this.length > this.rtpSession.pktBufBehavior || this.lastSeqNumber < 0)) { - - - //if(tmpNode.pktCount == compLen) { - if(RTPSession.rtpDebugLevel > 7) { - System.out.println("<- PktBuffer.popOldestFrame() returns frame"); - } - - DataFrame df = new DataFrame(retNode, this.p, - rtpSession.appIntf.frameSize(oldest.pkt.getPayloadType())); - - //DataFrame df = new DataFrame(retNode, this.p, 1); - popFrameQueueCleanup(retNode, df.lastSeqNum); - - return df; - - } else { - // If we get here we have little to show for. - if(RTPSession.rtpDebugLevel > 2) { - System.out.println("<- PktBuffer.popOldestFrame() returns null " + retNode.seqNum + " " + this.lastSeqNumber); - this.debugPrint(); - } - return null; - } - } - - /** - * Cleans the packet buffer before returning the frame, - * i.e. making sure the queue has a head etc. - * - * @param retNode the node that is about to be popped - * @param highestSeq the highest sequence number returned to the application - */ - private void popFrameQueueCleanup(PktBufNode retNode, int highestSeq) { - if(1 == length) { - //There's only one frame - newest = null; - oldest = null; - } else { - //There are more frames - oldest = oldest.prevFrameQueueNode; - oldest.nextFrameQueueNode = null; - } - - // Update counters - length--; - - //Find the highest sequence number associated with this timestamp - this.lastSeqNumber = highestSeq; - this.lastTimestamp = retNode.timeStamp; - } - - /** - * Returns the length of the packetbuffer. - * @return number of frames (complete or not) in packetbuffer. - */ - protected int getLength() { - return length; - } - - /** - * Checks whether a packet is not too late, i.e. the next packet has already been returned. - * @param timeStamp the RTP timestamp of the packet under consideration - * @param seqNum the sequence number of the packet under consideration - * @return true if newer packets have not been handed to the application - */ - protected boolean pktOnTime(long timeStamp, int seqNum) { - if(this.lastSeqNumber == -1) { - // First packet - return true; - } else { - if(seqNum >= this.lastSeqNumber) { - if(this.lastSeqNumber < 3 && timeStamp < this.lastTimestamp ) { - return false; - } - } else { - if(seqNum > 3 || timeStamp < this.lastTimestamp) { - return false; - } - } - } - return true; - } - - /** - * Prints out the packet buffer, oldest node first (on top). - */ - protected void debugPrint() { - System.out.println("PktBuffer.debugPrint() : length "+length+" SSRC "+SSRC+" lastSeqNum:"+lastSeqNumber); - PktBufNode tmpNode = oldest; - int i = 0; - while(tmpNode != null) { - //String str = tmpNode.timeStamp.toString(); - System.out.println(" " + i + " seqNum:"+tmpNode.seqNum+" timeStamp: " + tmpNode.timeStamp + " pktCount:" + tmpNode.pktCount ); - i++; - tmpNode = tmpNode.prevFrameQueueNode; - } - } -}