|
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 import java.net.DatagramPacket; |
|
22 |
|
23 /** |
|
24 * RtpPkt is the basic class for creating and parsing RTP packets. |
|
25 * |
|
26 * There are two ways of instantiating an RtpPkt. One is for packets that you |
|
27 * wish to send, which requires that you provide basic information about the |
|
28 * packet and a payload. Upon calling encode() the fields of the structure are |
|
29 * written into a bytebuffer, in the form that it would sent across the network, |
|
30 * excluding the UDP headers. |
|
31 * |
|
32 * The other way is by passing a bytebuffer. The assumption is that this is a |
|
33 * packet that has been received from the network, excluding UDP headers, and |
|
34 * the bytebuffer will be parsed into the correct fields. |
|
35 * |
|
36 * The class keeps track of changes. Therefore, modifications are possible after |
|
37 * calling encode(), if necessary, the raw version of the packet will be |
|
38 * regenerated on subsequent requests. |
|
39 * |
|
40 * @author Arne Kepp |
|
41 */ |
|
42 public class RtpPkt { |
|
43 /** Whether the packet has been changed since encode() */ |
|
44 private boolean rawPktCurrent = false; |
|
45 /** The version, always 2, 2 bits */ |
|
46 private int version = 2; // 2 bits |
|
47 /** Whether the packet is padded, 1 bit */ |
|
48 private int padding; // 1 bit |
|
49 /** Whether and extension is used, 1 bit */ |
|
50 private int extension = 0; // 1 bit |
|
51 /** Whether the packet is marked, 1 bit */ |
|
52 private int marker = 0; // 1 bit |
|
53 /** What payload type is used, 7 bits */ |
|
54 private int payloadType; // |
|
55 /** The sequence number, taken from RTP Session, 16 bits */ |
|
56 private int seqNumber; // 16 bits |
|
57 /** The RTP timestamp, 32bits */ |
|
58 private long timeStamp; // 32 bits |
|
59 /** The SSRC of the packet sender, 32 bits */ |
|
60 private long ssrc; // 32 bits |
|
61 /** SSRCs of contributing sources, 32xn bits, n<16 */ |
|
62 private long[] csrcArray = new long[0];// |
|
63 |
|
64 /** Contains the actual data (eventually) */ |
|
65 private byte[] rawPkt = null; |
|
66 |
|
67 |
|
68 |
|
69 /** The actual data, without any RTP stuff */ |
|
70 //private byte[] payload = null; |
|
71 private DatagramPacket datagramPacket; |
|
72 |
|
73 /** |
|
74 * Construct a packet-instance. The ByteBuffer required for UDP transmission |
|
75 * can afterwards be obtained from getRawPkt(). If you need to set |
|
76 * additional parameters, such as the marker bit or contributing sources, |
|
77 * you should do so before calling getRawPkt; |
|
78 * |
|
79 * @param aTimeStamp |
|
80 * RTP timestamp for data |
|
81 * @param syncSource |
|
82 * the SSRC, usually taken from RTPSession |
|
83 * @param seqNum |
|
84 * Sequency number |
|
85 * @param plt |
|
86 * Type of payload |
|
87 * @param pl |
|
88 * Payload, the actual data |
|
89 */ |
|
90 protected void initPacket(long aTimeStamp, long syncSource, int seqNum, int plt, |
|
91 byte[] pl) { |
|
92 int test = 0; |
|
93 test += setTimeStamp(aTimeStamp); |
|
94 test += setSsrc(syncSource); |
|
95 test += setSeqNumber(seqNum); |
|
96 test += setPayloadType(plt); |
|
97 //test += setPayload(pl); //TODO: faire d'une manière propre |
|
98 datagramPacket = null; |
|
99 if (test != 0) { |
|
100 System.out.println("RtpPkt() failed, check with checkPkt()"); |
|
101 } |
|
102 rawPktCurrent = true; |
|
103 if (RTPSession.rtpDebugLevel > 5) { |
|
104 System.out |
|
105 .println("<--> RtpPkt(aTimeStamp, syncSource, seqNum, plt, pl)"); |
|
106 } |
|
107 } |
|
108 |
|
109 /** |
|
110 * Construct a packet-instance from an raw packet (believed to be RTP). The |
|
111 * UDP-headers must be removed before invoking this method. Call checkPkt on |
|
112 * the instance to verify that it was successfully parsed. |
|
113 * |
|
114 * @param aRawPkt |
|
115 * The data-part of a UDP-packet believed to be RTP |
|
116 * @param packetSize |
|
117 * the number of valid octets in the packet, should be |
|
118 * aRawPkt.length |
|
119 */ |
|
120 public RtpPkt(byte[] aRawPkt, int packetSize, DatagramPacket packet) { |
|
121 initPacket(aRawPkt, packetSize, packet); |
|
122 } |
|
123 |
|
124 public RtpPkt() { |
|
125 } |
|
126 |
|
127 public RtpPkt(long aTimeStamp, long syncSource, int seqNum, int plt, |
|
128 byte[] pl) { |
|
129 initPacket(aTimeStamp, syncSource, seqNum, plt, pl); |
|
130 } |
|
131 |
|
132 protected void initPacket(byte[] aRawPkt, int packetSize, DatagramPacket packet) { |
|
133 if (RTPSession.rtpDebugLevel > 5) { |
|
134 System.out.println("-> RtpPkt(aRawPkt)"); |
|
135 } |
|
136 // Check size, need to have at least a complete header |
|
137 if (aRawPkt == null) { |
|
138 System.out.println("RtpPkt(byte[]) Packet null"); |
|
139 } |
|
140 |
|
141 int remOct = packetSize - 12; |
|
142 if (remOct >= 0) { |
|
143 rawPkt = aRawPkt; // Store it |
|
144 // Interrogate the packet |
|
145 datagramPacket = packet; |
|
146 sliceFirstLine(); |
|
147 if (version == 2) { |
|
148 sliceTimeStamp(); |
|
149 sliceSSRC(); |
|
150 if (remOct > 4 && getCsrcCount() > 0) { |
|
151 sliceCSRCs(); |
|
152 remOct -= csrcArray.length * 4; // 4 octets per CSRC |
|
153 } |
|
154 // TODO Extension |
|
155 /*if (remOct > 0) { |
|
156 slicePayload(remOct); |
|
157 }*/ |
|
158 |
|
159 // Sanity checks |
|
160 checkPkt(); |
|
161 |
|
162 // Mark the buffer as current |
|
163 rawPktCurrent = true; |
|
164 } else { |
|
165 System.out |
|
166 .println("RtpPkt(byte[]) Packet is not version 2, giving up."); |
|
167 } |
|
168 } else { |
|
169 System.out.println("RtpPkt(byte[]) Packet too small to be sliced"); |
|
170 } |
|
171 rawPktCurrent = true; |
|
172 if (RTPSession.rtpDebugLevel > 5) { |
|
173 System.out.println("<- RtpPkt(aRawPkt)"); |
|
174 } |
|
175 } |
|
176 |
|
177 /********************************************************************************************************* |
|
178 * Reading stuff |
|
179 *********************************************************************************************************/ |
|
180 protected int checkPkt() { |
|
181 // TODO, check for version 2 etc |
|
182 return 0; |
|
183 } |
|
184 |
|
185 protected int getHeaderLength() { |
|
186 // TODO include extension |
|
187 return 12 + 4 * getCsrcCount(); |
|
188 } |
|
189 |
|
190 protected int getPayloadLength() { |
|
191 return rawPkt.length - getHeaderLength(); |
|
192 } |
|
193 |
|
194 // public int getPaddingLength() { |
|
195 // return lenPadding; |
|
196 // } |
|
197 protected int getVersion() { |
|
198 return version; |
|
199 } |
|
200 |
|
201 // public boolean isPadded() { |
|
202 // if(lenPadding > 0) { |
|
203 // return true; |
|
204 // }else { |
|
205 // return false; |
|
206 // } |
|
207 // } |
|
208 // public int getHeaderExtension() { |
|
209 // TODO |
|
210 // } |
|
211 protected boolean isMarked() { |
|
212 return (marker != 0); |
|
213 } |
|
214 |
|
215 protected int getPayloadType() { |
|
216 return payloadType; |
|
217 } |
|
218 |
|
219 public int getSeqNumber() { |
|
220 return seqNumber; |
|
221 } |
|
222 |
|
223 protected long getTimeStamp() { |
|
224 return timeStamp; |
|
225 } |
|
226 |
|
227 protected long getSsrc() { |
|
228 return ssrc; |
|
229 } |
|
230 |
|
231 protected int getCsrcCount() { |
|
232 if (csrcArray != null) { |
|
233 return csrcArray.length; |
|
234 } else { |
|
235 return 0; |
|
236 } |
|
237 } |
|
238 |
|
239 protected long[] getCsrcArray() { |
|
240 return csrcArray; |
|
241 } |
|
242 |
|
243 /** |
|
244 * Encodes the a |
|
245 */ |
|
246 protected byte[] encode() { |
|
247 if (!rawPktCurrent || rawPkt == null) { |
|
248 writePkt(); |
|
249 android.util.Log.d("RtpPkt", "writePkt"); |
|
250 } |
|
251 return rawPkt; |
|
252 } |
|
253 |
|
254 /* For debugging purposes */ |
|
255 protected void printPkt() { |
|
256 System.out |
|
257 .print("V:" + version + " P:" + padding + " EXT:" + extension); |
|
258 System.out.println(" CC:" + getCsrcCount() + " M:" + marker + " PT:" |
|
259 + payloadType + " SN: " + seqNumber); |
|
260 System.out.println("Timestamp:" + timeStamp |
|
261 + "(long output as int, may be 2s complement)"); |
|
262 System.out.println("SSRC:" + ssrc |
|
263 + "(long output as int, may be 2s complement)"); |
|
264 for (int i = 0; i < getCsrcCount(); i++) { |
|
265 System.out.println("CSRC:" + csrcArray[i] |
|
266 + "(long output as int, may be 2s complement)"); |
|
267 } |
|
268 |
|
269 } |
|
270 |
|
271 /********************************************************************************************************* |
|
272 * Setting stuff |
|
273 *********************************************************************************************************/ |
|
274 protected void setMarked(boolean mark) { |
|
275 rawPktCurrent = false; |
|
276 if (mark) { |
|
277 marker = 1; |
|
278 } else { |
|
279 marker = 0; |
|
280 } |
|
281 } |
|
282 |
|
283 // public int setHeaderExtension() { |
|
284 // TODO |
|
285 // } |
|
286 public int setPayloadType(int plType) { |
|
287 int temp = (plType & 0x0000007F); // 7 bits, checks in RTPSession as |
|
288 // well. |
|
289 if (temp == plType) { |
|
290 rawPktCurrent = false; |
|
291 payloadType = temp; |
|
292 return 0; |
|
293 } else { |
|
294 return -1; |
|
295 } |
|
296 } |
|
297 |
|
298 protected int setSeqNumber(int number) { |
|
299 if (number <= 65536 && number >= 0) { |
|
300 rawPktCurrent = false; |
|
301 seqNumber = number; |
|
302 return 0; |
|
303 } else { |
|
304 System.out.println("RtpPkt.setSeqNumber: invalid number"); |
|
305 return -1; |
|
306 } |
|
307 } |
|
308 |
|
309 protected int setTimeStamp(long time) { |
|
310 rawPktCurrent = false; |
|
311 timeStamp = time; |
|
312 return 0; // Naive for now |
|
313 } |
|
314 |
|
315 protected int setSsrc(long source) { |
|
316 rawPktCurrent = false; |
|
317 ssrc = source; |
|
318 return 0; // Naive for now |
|
319 } |
|
320 |
|
321 protected int setCsrcs(long[] contributors) { |
|
322 if (contributors.length <= 16) { |
|
323 csrcArray = contributors; |
|
324 return 0; |
|
325 } else { |
|
326 System.out |
|
327 .println("RtpPkt.setCsrcs: Cannot have more than 16 CSRCs"); |
|
328 return -1; |
|
329 } |
|
330 } |
|
331 |
|
332 /*protected int setPayload(byte[] data) { |
|
333 // TODO Padding |
|
334 if (data.length < (1500 - 12)) { |
|
335 rawPktCurrent = false; |
|
336 payload = data; |
|
337 return 0; |
|
338 } else { |
|
339 System.out |
|
340 .println("RtpPkt.setPayload: Cannot carry more than 1480 bytes for now."); |
|
341 return -1; |
|
342 } |
|
343 }*/ |
|
344 |
|
345 public byte[] getPayload() { |
|
346 return rawPkt; |
|
347 } |
|
348 |
|
349 /********************************************************************************************************* |
|
350 * Private functions |
|
351 *********************************************************************************************************/ |
|
352 // Generate a bytebyffer representing the packet, store it. |
|
353 private void writePkt() { |
|
354 int bytes = getPayloadLength(); |
|
355 int headerLen = getHeaderLength(); |
|
356 int csrcLen = getCsrcCount(); |
|
357 rawPkt = new byte[headerLen + bytes]; |
|
358 |
|
359 // The first line contains, version and various bits |
|
360 writeFirstLine(); |
|
361 byte[] someBytes = StaticProcs.uIntLongToByteWord(timeStamp); |
|
362 for (int i = 0; i < 4; i++) { |
|
363 rawPkt[i + 4] = someBytes[i]; |
|
364 } |
|
365 // System.out.println("writePkt timeStamp:" + rawPkt[7]); |
|
366 |
|
367 someBytes = StaticProcs.uIntLongToByteWord(ssrc); |
|
368 System.arraycopy(someBytes, 0, rawPkt, 8, 4); |
|
369 // System.out.println("writePkt ssrc:" + rawPkt[11]); |
|
370 |
|
371 for (int i = 0; i < csrcLen; i++) { |
|
372 someBytes = StaticProcs.uIntLongToByteWord(csrcArray[i]); |
|
373 System.arraycopy(someBytes, 0, rawPkt, 12 + 4 * i, 4); |
|
374 } |
|
375 // TODO Extension |
|
376 |
|
377 // Payload |
|
378 //System.arraycopy(payload, 0, rawPkt, headerLen, bytes); |
|
379 rawPktCurrent = true; |
|
380 } |
|
381 |
|
382 void writeHeader() { |
|
383 //int bytes = rawPkt.length - 12; |
|
384 //int headerLen = getHeaderLength(); |
|
385 int csrcLen = getCsrcCount(); |
|
386 |
|
387 // The first line contains, version and various bits |
|
388 writeFirstLine(); |
|
389 StaticProcs.uIntLongToByteWord(timeStamp, rawPkt, 4); |
|
390 // System.out.println("writePkt timeStamp:" + rawPkt[7]); |
|
391 |
|
392 StaticProcs.uIntLongToByteWord(ssrc, rawPkt, 8); |
|
393 //System.arraycopy(someBytes, 0, rawPkt, 8, 4); |
|
394 // System.out.println("writePkt ssrc:" + rawPkt[11]); |
|
395 |
|
396 for (int i = 0; i < csrcLen; i++) { |
|
397 StaticProcs.uIntLongToByteWord(csrcArray[i], rawPkt, 12 + 4 * i); |
|
398 //System.arraycopy(someBytes, 0, rawPkt, 12 + 4 * i, 4); |
|
399 } |
|
400 // TODO Extension |
|
401 |
|
402 // Payload |
|
403 //System.arraycopy(payload, 0, rawPkt, headerLen, bytes); |
|
404 rawPktCurrent = true; |
|
405 } |
|
406 |
|
407 // Writes the first 4 octets of the RTP packet |
|
408 protected void writeFirstLine() { |
|
409 byte aByte = 0; |
|
410 aByte |= (version << 6); |
|
411 aByte |= (padding << 5); |
|
412 aByte |= (extension << 4); |
|
413 aByte |= (getCsrcCount()); |
|
414 rawPkt[0] = aByte; |
|
415 aByte = 0; |
|
416 aByte |= (marker << 7); |
|
417 aByte |= payloadType; |
|
418 rawPkt[1] = aByte; |
|
419 StaticProcs.uIntIntToByteWord(seqNumber, rawPkt, 2); |
|
420 } |
|
421 |
|
422 // Picks apart the first 4 octets of an RTP packet |
|
423 private void sliceFirstLine() { |
|
424 version = ((rawPkt[0] & 0xC0) >>> 6); |
|
425 padding = ((rawPkt[0] & 0x20) >>> 5); |
|
426 extension = ((rawPkt[0] & 0x10) >>> 4); |
|
427 //csrcArray = new long[(rawPkt[0] & 0x0F)]; |
|
428 marker = ((rawPkt[1] & 0x80) >> 7); |
|
429 payloadType = (rawPkt[1] & 0x7F); |
|
430 seqNumber = StaticProcs.bytesToUIntInt(rawPkt, 2); |
|
431 } |
|
432 |
|
433 // Takes the 4 octets representing the timestamp |
|
434 private void sliceTimeStamp() { |
|
435 timeStamp = StaticProcs.bytesToUIntLong(rawPkt, 4); |
|
436 } |
|
437 |
|
438 // Takes the 4 octets representing the SSRC |
|
439 private void sliceSSRC() { |
|
440 ssrc = StaticProcs.bytesToUIntLong(rawPkt, 8); |
|
441 } |
|
442 |
|
443 // Check the length of the csrcArray (set during sliceFirstLine) |
|
444 private void sliceCSRCs() { |
|
445 for (int i = 0; i < csrcArray.length; i++) { |
|
446 ssrc = StaticProcs.bytesToUIntLong(rawPkt, i * 4 + 12); |
|
447 } |
|
448 } |
|
449 |
|
450 // Extensions //TODO |
|
451 /*private void slicePayload(int bytes) { |
|
452 payload = new byte[bytes]; |
|
453 int headerLen = getHeaderLength(); |
|
454 |
|
455 System.arraycopy(rawPkt, headerLen, payload, 0, bytes); |
|
456 }*/ |
|
457 |
|
458 public void setRawPkt(byte[] rawPkt) { |
|
459 this.rawPkt = rawPkt; |
|
460 } |
|
461 |
|
462 public DatagramPacket getDatagramPacket() { |
|
463 return datagramPacket; |
|
464 } |
|
465 } |