13
|
1 |
/**
|
|
2 |
* Java RTP Library (jlibrtp)
|
|
3 |
* Copyright (C) 2006 Arne Kepp
|
|
4 |
*
|
|
5 |
* This library is free software; you can redistribute it and/or
|
|
6 |
* modify it under the terms of the GNU Lesser General Public
|
|
7 |
* License as published by the Free Software Foundation; either
|
|
8 |
* version 2.1 of the License, or (at your option) any later version.
|
|
9 |
*
|
|
10 |
* This library is distributed in the hope that it will be useful,
|
|
11 |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
12 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
13 |
* Lesser General Public License for more details.
|
|
14 |
*
|
|
15 |
* You should have received a copy of the GNU Lesser General Public
|
|
16 |
* License along with this library; if not, write to the Free Software
|
|
17 |
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
18 |
*/
|
|
19 |
package jlibrtp;
|
|
20 |
|
|
21 |
/**
|
|
22 |
* RtpPkt is the basic class for creating and parsing RTP packets.
|
|
23 |
*
|
|
24 |
* There are two ways of instantiating an RtpPkt. One is for packets that you wish to send,
|
|
25 |
* which requires that you provide basic information about the packet and a payload. Upon calling
|
|
26 |
* encode() the fields of the structure are written into a bytebuffer, in the form that it would
|
|
27 |
* sent across the network, excluding the UDP headers.
|
|
28 |
*
|
|
29 |
* The other way is by passing a bytebuffer. The assumption is that this is a packet
|
|
30 |
* that has been received from the network, excluding UDP headers, and the bytebuffer will
|
|
31 |
* be parsed into the correct fields.
|
|
32 |
*
|
|
33 |
* The class keeps track of changes. Therefore, modifications are possible after calling encode(),
|
|
34 |
* if necessary, the raw version of the packet will be regenerated on subsequent requests.
|
|
35 |
*
|
|
36 |
* @author Arne Kepp
|
|
37 |
*/
|
|
38 |
public class RtpPkt {
|
|
39 |
/** Whether the packet has been changed since encode() */
|
|
40 |
private boolean rawPktCurrent = false;
|
|
41 |
/** The version, always 2, 2 bits */
|
|
42 |
private int version = 2; //2 bits
|
|
43 |
/** Whether the packet is padded, 1 bit */
|
|
44 |
private int padding; //1 bit
|
|
45 |
/** Whether and extension is used, 1 bit */
|
|
46 |
private int extension = 0; //1 bit
|
|
47 |
/** Whether the packet is marked, 1 bit */
|
|
48 |
private int marker = 0; //1 bit
|
|
49 |
/** What payload type is used, 7 bits */
|
|
50 |
private int payloadType; //
|
|
51 |
/** The sequence number, taken from RTP Session, 16 bits */
|
|
52 |
private int seqNumber; //16 bits
|
|
53 |
/** The RTP timestamp, 32bits */
|
|
54 |
private long timeStamp; //32 bits
|
|
55 |
/** The SSRC of the packet sender, 32 bits*/
|
|
56 |
private long ssrc; //32 bits
|
|
57 |
/** SSRCs of contributing sources, 32xn bits, n<16 */
|
|
58 |
private long[] csrcArray = null;//
|
|
59 |
|
|
60 |
/** Contains the actual data (eventually) */
|
|
61 |
private byte[] rawPkt = null;
|
|
62 |
/** The actual data, without any RTP stuff */
|
|
63 |
private byte[] payload = null;
|
|
64 |
|
|
65 |
/**
|
|
66 |
* Construct a packet-instance. The ByteBuffer required for UDP transmission can afterwards be obtained from getRawPkt(). If you need to set additional parameters, such as the marker bit or contributing sources, you should do so before calling getRawPkt;
|
|
67 |
*
|
|
68 |
* @param aTimeStamp RTP timestamp for data
|
|
69 |
* @param syncSource the SSRC, usually taken from RTPSession
|
|
70 |
* @param seqNum Sequency number
|
|
71 |
* @param plt Type of payload
|
|
72 |
* @param pl Payload, the actual data
|
|
73 |
*/
|
|
74 |
protected RtpPkt(long aTimeStamp, long syncSource, int seqNum, int plt, byte[] pl){
|
|
75 |
int test = 0;
|
|
76 |
test += setTimeStamp(aTimeStamp);
|
|
77 |
test += setSsrc(syncSource);
|
|
78 |
test += setSeqNumber(seqNum);
|
|
79 |
test += setPayloadType(plt);
|
|
80 |
test += setPayload(pl);
|
|
81 |
if(test != 0) {
|
|
82 |
System.out.println("RtpPkt() failed, check with checkPkt()");
|
|
83 |
}
|
|
84 |
rawPktCurrent = true;
|
|
85 |
if( RTPSession.rtpDebugLevel > 5) {
|
|
86 |
System.out.println("<--> RtpPkt(aTimeStamp, syncSource, seqNum, plt, pl)");
|
|
87 |
}
|
|
88 |
}
|
|
89 |
/**
|
|
90 |
* Construct a packet-instance from an raw packet (believed to be RTP). The UDP-headers must be removed before invoking this method. Call checkPkt on the instance to verify that it was successfully parsed.
|
|
91 |
*
|
|
92 |
* @param aRawPkt The data-part of a UDP-packet believed to be RTP
|
|
93 |
* @param packetSize the number of valid octets in the packet, should be aRawPkt.length
|
|
94 |
*/
|
|
95 |
protected RtpPkt(byte[] aRawPkt, int packetSize){
|
|
96 |
if( RTPSession.rtpDebugLevel > 5) {
|
|
97 |
System.out.println("-> RtpPkt(aRawPkt)");
|
|
98 |
}
|
|
99 |
//Check size, need to have at least a complete header
|
|
100 |
if(aRawPkt == null) {
|
|
101 |
System.out.println("RtpPkt(byte[]) Packet null");
|
|
102 |
}
|
|
103 |
|
|
104 |
int remOct = packetSize - 12;
|
|
105 |
if(remOct >= 0) {
|
|
106 |
rawPkt = aRawPkt; //Store it
|
|
107 |
//Interrogate the packet
|
|
108 |
sliceFirstLine();
|
|
109 |
if(version == 2) {
|
|
110 |
sliceTimeStamp();
|
|
111 |
sliceSSRC();
|
|
112 |
if(remOct > 4 && getCsrcCount() > 0) {
|
|
113 |
sliceCSRCs();
|
|
114 |
remOct -= csrcArray.length * 4; //4 octets per CSRC
|
|
115 |
}
|
|
116 |
// TODO Extension
|
|
117 |
if(remOct > 0) {
|
|
118 |
slicePayload(remOct);
|
|
119 |
}
|
|
120 |
|
|
121 |
//Sanity checks
|
|
122 |
checkPkt();
|
|
123 |
|
|
124 |
//Mark the buffer as current
|
|
125 |
rawPktCurrent = true;
|
|
126 |
} else {
|
|
127 |
System.out.println("RtpPkt(byte[]) Packet is not version 2, giving up.");
|
|
128 |
}
|
|
129 |
} else {
|
|
130 |
System.out.println("RtpPkt(byte[]) Packet too small to be sliced");
|
|
131 |
}
|
|
132 |
rawPktCurrent = true;
|
|
133 |
if( RTPSession.rtpDebugLevel > 5) {
|
|
134 |
System.out.println("<- RtpPkt(aRawPkt)");
|
|
135 |
}
|
|
136 |
}
|
|
137 |
|
|
138 |
/*********************************************************************************************************
|
|
139 |
* Reading stuff
|
|
140 |
*********************************************************************************************************/
|
|
141 |
protected int checkPkt() {
|
|
142 |
//TODO, check for version 2 etc
|
|
143 |
return 0;
|
|
144 |
}
|
|
145 |
protected int getHeaderLength() {
|
|
146 |
//TODO include extension
|
|
147 |
return 12 + 4*getCsrcCount();
|
|
148 |
}
|
|
149 |
protected int getPayloadLength() {
|
|
150 |
return payload.length;
|
|
151 |
}
|
|
152 |
//public int getPaddingLength() {
|
|
153 |
// return lenPadding;
|
|
154 |
//}
|
|
155 |
protected int getVersion() {
|
|
156 |
return version;
|
|
157 |
}
|
|
158 |
//public boolean isPadded() {
|
|
159 |
// if(lenPadding > 0) {
|
|
160 |
// return true;
|
|
161 |
// }else {
|
|
162 |
// return false;
|
|
163 |
// }
|
|
164 |
//}
|
|
165 |
//public int getHeaderExtension() {
|
|
166 |
//TODO
|
|
167 |
//}
|
|
168 |
protected boolean isMarked() {
|
|
169 |
return (marker != 0);
|
|
170 |
}
|
|
171 |
protected int getPayloadType() {
|
|
172 |
return payloadType;
|
|
173 |
}
|
|
174 |
|
|
175 |
protected int getSeqNumber() {
|
|
176 |
return seqNumber;
|
|
177 |
}
|
|
178 |
protected long getTimeStamp() {
|
|
179 |
return timeStamp;
|
|
180 |
}
|
|
181 |
protected long getSsrc() {
|
|
182 |
return ssrc;
|
|
183 |
}
|
|
184 |
|
|
185 |
protected int getCsrcCount() {
|
|
186 |
if(csrcArray != null) {
|
|
187 |
return csrcArray.length;
|
|
188 |
}else{
|
|
189 |
return 0;
|
|
190 |
}
|
|
191 |
}
|
|
192 |
protected long[] getCsrcArray() {
|
|
193 |
return csrcArray;
|
|
194 |
}
|
|
195 |
|
|
196 |
/**
|
|
197 |
* Encodes the a
|
|
198 |
*/
|
|
199 |
protected byte[] encode() {
|
|
200 |
if(! rawPktCurrent || rawPkt == null) {
|
|
201 |
writePkt();
|
|
202 |
}
|
|
203 |
return rawPkt;
|
|
204 |
}
|
|
205 |
|
|
206 |
/* For debugging purposes */
|
|
207 |
protected void printPkt() {
|
|
208 |
System.out.print("V:" + version + " P:" + padding + " EXT:" + extension);
|
|
209 |
System.out.println(" CC:" + getCsrcCount() + " M:"+ marker +" PT:" + payloadType + " SN: "+ seqNumber);
|
|
210 |
System.out.println("Timestamp:" + timeStamp + "(long output as int, may be 2s complement)");
|
|
211 |
System.out.println("SSRC:" + ssrc + "(long output as int, may be 2s complement)");
|
|
212 |
for(int i=0;i<getCsrcCount();i++) {
|
|
213 |
System.out.println("CSRC:" + csrcArray[i] + "(long output as int, may be 2s complement)");
|
|
214 |
}
|
|
215 |
//TODO Extension
|
|
216 |
System.out.println("Payload, first four bytes: " + payload[0] + " " + payload[1] + " " + payload[2] + " " + payload[3]);
|
|
217 |
}
|
|
218 |
/*********************************************************************************************************
|
|
219 |
* Setting stuff
|
|
220 |
*********************************************************************************************************/
|
|
221 |
protected void setMarked(boolean mark) {
|
|
222 |
rawPktCurrent = false;
|
|
223 |
if(mark) {
|
|
224 |
marker = 1;
|
|
225 |
} else {
|
|
226 |
marker = 0;
|
|
227 |
}
|
|
228 |
}
|
|
229 |
//public int setHeaderExtension() {
|
|
230 |
//TODO
|
|
231 |
//}
|
|
232 |
protected int setPayloadType(int plType) {
|
|
233 |
int temp = (plType & 0x0000007F); // 7 bits, checks in RTPSession as well.
|
|
234 |
if(temp == plType) {
|
|
235 |
rawPktCurrent = false;
|
|
236 |
payloadType = temp;
|
|
237 |
return 0;
|
|
238 |
} else {
|
|
239 |
return -1;
|
|
240 |
}
|
|
241 |
}
|
|
242 |
|
|
243 |
protected int setSeqNumber(int number) {
|
|
244 |
if(number <= 65536 && number >= 0) {
|
|
245 |
rawPktCurrent = false;
|
|
246 |
seqNumber = number;
|
|
247 |
return 0;
|
|
248 |
} else {
|
|
249 |
System.out.println("RtpPkt.setSeqNumber: invalid number");
|
|
250 |
return -1;
|
|
251 |
}
|
|
252 |
}
|
|
253 |
|
|
254 |
protected int setTimeStamp(long time) {
|
|
255 |
rawPktCurrent = false;
|
|
256 |
timeStamp = time;
|
|
257 |
return 0; //Naive for now
|
|
258 |
}
|
|
259 |
|
|
260 |
protected int setSsrc(long source) {
|
|
261 |
rawPktCurrent = false;
|
|
262 |
ssrc = source;
|
|
263 |
return 0; //Naive for now
|
|
264 |
}
|
|
265 |
|
|
266 |
protected int setCsrcs(long[] contributors) {
|
|
267 |
if(contributors.length <= 16) {
|
|
268 |
csrcArray = contributors;
|
|
269 |
return 0;
|
|
270 |
} else {
|
|
271 |
System.out.println("RtpPkt.setCsrcs: Cannot have more than 16 CSRCs");
|
|
272 |
return -1;
|
|
273 |
}
|
|
274 |
}
|
|
275 |
|
|
276 |
protected int setPayload(byte[] data) {
|
|
277 |
// TODO Padding
|
|
278 |
if(data.length < (1500 - 12)) {
|
|
279 |
rawPktCurrent = false;
|
|
280 |
payload = data;
|
|
281 |
return 0;
|
|
282 |
} else {
|
|
283 |
System.out.println("RtpPkt.setPayload: Cannot carry more than 1480 bytes for now.");
|
|
284 |
return -1;
|
|
285 |
}
|
|
286 |
}
|
|
287 |
protected byte[] getPayload() {
|
|
288 |
return payload;
|
|
289 |
}
|
|
290 |
|
|
291 |
/*********************************************************************************************************
|
|
292 |
* Private functions
|
|
293 |
*********************************************************************************************************/
|
|
294 |
//Generate a bytebyffer representing the packet, store it.
|
|
295 |
private void writePkt() {
|
|
296 |
int bytes = getPayloadLength();
|
|
297 |
int headerLen = getHeaderLength();
|
|
298 |
int csrcLen = getCsrcCount();
|
|
299 |
rawPkt = new byte[headerLen + bytes];
|
|
300 |
|
|
301 |
// The first line contains, version and various bits
|
|
302 |
writeFirstLine();
|
|
303 |
byte[] someBytes = StaticProcs.uIntLongToByteWord(timeStamp);
|
|
304 |
for(int i=0;i<4;i++) {
|
|
305 |
rawPkt[i + 4] = someBytes[i];
|
|
306 |
}
|
|
307 |
//System.out.println("writePkt timeStamp:" + rawPkt[7]);
|
|
308 |
|
|
309 |
someBytes = StaticProcs.uIntLongToByteWord(ssrc);
|
|
310 |
System.arraycopy(someBytes, 0, rawPkt, 8, 4);
|
|
311 |
//System.out.println("writePkt ssrc:" + rawPkt[11]);
|
|
312 |
|
|
313 |
for(int i=0; i<csrcLen ; i++) {
|
|
314 |
someBytes = StaticProcs.uIntLongToByteWord(csrcArray[i]);
|
|
315 |
System.arraycopy(someBytes, 0, rawPkt, 12 + 4*i, 4);
|
|
316 |
}
|
|
317 |
// TODO Extension
|
|
318 |
|
|
319 |
//Payload
|
|
320 |
System.arraycopy(payload, 0, rawPkt, headerLen, bytes);
|
|
321 |
rawPktCurrent = true;
|
|
322 |
}
|
|
323 |
//Writes the first 4 octets of the RTP packet
|
|
324 |
private void writeFirstLine() {
|
|
325 |
byte aByte = 0;
|
|
326 |
aByte |=(version << 6);
|
|
327 |
aByte |=(padding << 5);
|
|
328 |
aByte |=(extension << 4);
|
|
329 |
aByte |=(getCsrcCount());
|
|
330 |
rawPkt[0] = aByte;
|
|
331 |
aByte = 0;
|
|
332 |
aByte |=(marker << 7);
|
|
333 |
aByte |= payloadType;
|
|
334 |
rawPkt[1] = aByte;
|
|
335 |
byte[] someBytes = StaticProcs.uIntIntToByteWord(seqNumber);
|
|
336 |
rawPkt[2] = someBytes[0];
|
|
337 |
rawPkt[3] = someBytes[1];
|
|
338 |
}
|
|
339 |
//Picks apart the first 4 octets of an RTP packet
|
|
340 |
private void sliceFirstLine() {
|
|
341 |
version = ((rawPkt[0] & 0xC0) >>> 6);
|
|
342 |
padding = ((rawPkt[0] & 0x20) >>> 5);
|
|
343 |
extension = ((rawPkt[0] & 0x10) >>> 4);
|
|
344 |
csrcArray = new long[(rawPkt[0] & 0x0F)];
|
|
345 |
marker = ((rawPkt[1] & 0x80) >> 7);
|
|
346 |
payloadType = (rawPkt[1] & 0x7F);
|
|
347 |
seqNumber = StaticProcs.bytesToUIntInt(rawPkt, 2);
|
|
348 |
}
|
|
349 |
//Takes the 4 octets representing the timestamp
|
|
350 |
private void sliceTimeStamp() {
|
|
351 |
timeStamp = StaticProcs.bytesToUIntLong(rawPkt, 4);
|
|
352 |
}
|
|
353 |
//Takes the 4 octets representing the SSRC
|
|
354 |
private void sliceSSRC() {
|
|
355 |
ssrc = StaticProcs.bytesToUIntLong(rawPkt,8);
|
|
356 |
}
|
|
357 |
//Check the length of the csrcArray (set during sliceFirstLine)
|
|
358 |
private void sliceCSRCs() {
|
|
359 |
for(int i=0; i< csrcArray.length; i++) {
|
|
360 |
ssrc = StaticProcs.bytesToUIntLong(rawPkt, i*4 + 12);
|
|
361 |
}
|
|
362 |
}
|
|
363 |
//Extensions //TODO
|
|
364 |
private void slicePayload(int bytes) {
|
|
365 |
payload = new byte[bytes];
|
|
366 |
int headerLen = getHeaderLength();
|
|
367 |
|
|
368 |
System.arraycopy(rawPkt, headerLen, payload, 0, bytes);
|
|
369 |
}
|
|
370 |
} |