src/jlibrtp/PktBuffer.java
changeset 214 2bf440c54ca5
parent 213 9bdff6cbd120
child 215 5db64229be69
--- 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;
-		}
-	}
-}