|
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 } |