diff -r c9ff263c29ad -r e684f11070d5 src/jlibrtp/DataFrame.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/jlibrtp/DataFrame.java Sat Mar 14 22:15:41 2009 +0100 @@ -0,0 +1,273 @@ +/** + * 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; + + +/** + * Data structure to hold a complete frame if frame reconstruction + * is enabled, or the data from an individual packet if it is not + * + * It also contains most of the data from the individual packets + * that it is based on. + * + * @author Arne Kepp + */ +public class DataFrame { + /** The share RTP timestamp */ + private long rtpTimestamp; + /** The calculated UNIX timestamp, guessed after 2 Sender Reports */ + private long timestamp = -1; + /** the SSRC from which this frame originated */ + private long SSRC; + /** contributing CSRCs, only read from the first packet */ + private long[] CSRCs; + /** RTP payload type */ + private int payloadType; + /** The marks on individual packets, ordered */ + private boolean[] marks; + /** Whether any packets were marked or not */ + private boolean anyMarked = false; + /** Whether the frame contains the expected number of packets */ + private int isComplete = 0; + //private int dataLength; + /** The data from the individual packets, ordered */ + private byte[][] data; + /** The sequence numbers of the individual packets, ordered */ + private int[] seqNum; + /** The total amount of data bytes in this frame */ + private int totalLength = 0; + /** The last sequence number in this frame */ + protected int lastSeqNum; + /** The first sequence number in this frame */ + protected int firstSeqNum; + /** The number of packets expected for a complete frame */ + protected int noPkts; + + /** + * The usual way to construct a frame is by giving it a PktBufNode, + * which contains links to all the other pkts that make it up. + */ + protected DataFrame(PktBufNode aBufNode, Participant p, int noPkts) { + if(RTPSession.rtpDebugLevel > 6) { + System.out.println("-> DataFrame(PktBufNode, noPkts = " + noPkts +")"); + } + this.noPkts = noPkts; + RtpPkt aPkt = aBufNode.pkt; + int pktCount = aBufNode.pktCount; + firstSeqNum = aBufNode.pktCount; + + // All this data should be shared, so we just get it from the first one + this.rtpTimestamp = aBufNode.timeStamp; + SSRC = aPkt.getSsrc(); + CSRCs = aPkt.getCsrcArray(); + + // Check whether we can compute an NTPish timestamp? Requires two SR reports + if(p.ntpGradient > 0) { + //System.out.print(Long.toString(p.ntpOffset)+" " + timestamp = p.ntpOffset + (long) (p.ntpGradient*(double)(this.rtpTimestamp-p.lastSRRtpTs)); + } + + // Make data the right length + int payloadLength = aPkt.getPayloadLength(); + //System.out.println("aBufNode.pktCount " + aBufNode.pktCount); + data = new byte[aBufNode.pktCount][payloadLength]; + seqNum = new int[aBufNode.pktCount]; + marks = new boolean[aBufNode.pktCount]; + + // Concatenate the data of the packets + int i; + for(i=0; i< pktCount; i++) { + aPkt = aBufNode.pkt; + byte[] temp = aPkt.getPayload(); + totalLength += temp.length; + if(temp.length == payloadLength) { + data[i] = temp; + } else if(temp.length < payloadLength){ + System.arraycopy(temp, 0, data[i], 0, temp.length); + } else { + System.out.println("DataFrame() received node structure with increasing packet payload size."); + } + //System.out.println("i " + i + " seqNum[i] " + seqNum[i] + " aBufNode" + aBufNode); + seqNum[i] = aBufNode.seqNum; + marks[i] = aBufNode.pkt.isMarked(); + if(marks[i]) + anyMarked = true; + + // Get next node + aBufNode = aBufNode.nextFrameNode; + } + + lastSeqNum = seqNum[i - 1]; + + if(noPkts > 0) { + int seqDiff = firstSeqNum - lastSeqNum; + if(seqDiff < 0) + seqDiff = (Integer.MAX_VALUE - firstSeqNum) + lastSeqNum; + if(seqDiff == pktCount && pktCount == noPkts) + isComplete = 1; + } else { + isComplete = -1; + } + + if(RTPSession.rtpDebugLevel > 6) { + System.out.println("<- DataFrame(PktBufNode, noPkt), data length: " + data.length); + } + } + + /** + * Returns a two dimensial array where the first dimension represents individual + * packets, from which the frame is made up, in order of increasing sequence number. + * These indeces can be matched to the sequence numbers returned by sequenceNumbers(). + * + * @return 2-dim array with raw data from packets + */ + public byte[][] getData() { + return this.data; + } + + /** + * Returns a concatenated version of the data from getData() + * It ignores missing sequence numbers, but then isComplete() + * will return false provided that RTPAppIntf.frameSize() + * provides a non-negative number for this payload type. + * + * @return byte[] with all the data concatenated + */ + public byte[] getConcatenatedData() { + if(this.noPkts < 2) { + byte[] ret = new byte[this.totalLength]; + int pos = 0; + + for(int i=0; i totalLength) + length = totalLength - pos; + + System.arraycopy(data[i], 0, ret, pos, length); + pos += data[i].length; + } + return ret; + } else { + return data[0]; + } + } + + /** + * If two SR packet have been received jlibrtp will attempt to calculate + * the local UNIX timestamp (in milliseconds) of all packets received. + * + * This value should ideally correspond to the local time when the + * SSRC sent the packet. Note that the source may not be reliable. + * + * Returns -1 if less than two SRs have been received + * + * @return the UNIX timestamp, similar to System.currentTimeMillis() or -1; + */ + public long timestamp() { + return this.timestamp; + + } + + /** + * Returns the RTP timestamp of all the packets in the frame. + * + * @return unmodified RTP timestamp + */ + public long rtpTimestamp() { + return this.rtpTimestamp; + } + + /** + * Returns the payload type of the packets + * + * @return the payload type of the packets + */ + public int payloadType() { + return this.payloadType; + } + + /** + * Returns an array whose values, for the same index, correpond to the + * sequence number of the packet from which the data came. + * + * This information can be valuable in conjunction with getData(), + * to identify what parts of a frame are missing. + * + * @return array with sequence numbers + */ + public int[] sequenceNumbers() { + return seqNum; + } + + /** + * Returns an array whose values, for the same index, correpond to + * whether the data was marked or not. + * + * This information can be valuable in conjunction with getData(). + * + * @return array of booleans + */ + public boolean[] marks() { + return this.marks; + } + + /** + * Returns true if any packet in the frame was marked. + * + * This function should be used if all your frames fit + * into single packets. + * + * @return true if any packet was marked, false otherwise + */ + public boolean marked() { + return this.anyMarked; + } + + /** + * The SSRC associated with this frame. + * + * @return the ssrc that created this frame + */ + public long ssrc() { + return this.SSRC; + } + + /** + * The SSRCs that contributed to this frame + * + * @return an array of contributing SSRCs, or null + */ + public long[] csrcs() { + return this.CSRCs; + } + + /** + * Checks whether the difference in sequence numbers corresponds + * to the number of packets received for the current timestamp, + * and whether this value corresponds to the expected number of + * packets. + * + * @return true if the right number of packets make up the frame + */ + public int complete() { + return this.isComplete; + } +}