Newer
Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package org.jitsi.impl.neomedia;
/**
* When using TransformConnector, a RTP/RTCP packet is represented using
* RawPacket. RawPacket stores the buffer holding the RTP/RTCP packet, as well
* as the inner offset and length of RTP/RTCP packet data.
*
* After transformation, data is also store in RawPacket objects, either the
* original RawPacket (in place transformation), or a newly created RawPacket.
*
* Besides packet info storage, RawPacket also provides some other operations
* such as readInt() to ease the development process.
*
* @author Werner Dittmann (Werner.Dittmann@t-online.de)
* @author Bing SU (nova.su@gmail.com)
* @author Emil Ivov
* @author Damian Minkov
*/
public class RawPacket
{
/**
* The size of the fixed part of the RTP header as defined by RFC 3550.
*/
public static final int FIXED_HEADER_SIZE = 12;
/**
* The size of the extension header as defined by RFC 3550.
*/
public static final int EXT_HEADER_SIZE = 4;
/**
* Byte array storing the content of this Packet
*/
private byte[] buffer;
/**
* Start offset of the packet data inside buffer.
* Usually this value would be 0. But in order to be compatible with
* RTPManager we store this info. (Not assuming the offset is always zero)
*/
private int offset;
/**
* Length of this packet's data
*/
private int length;
/**
* Construct an empty RawPacket
*
*/
public RawPacket()
{
}
/**
* Construct a RawPacket using specified value.
*
* @param buffer Byte array holding the content of this Packet
* @param offset Start offset of packet content inside buffer
* @param length Length of the packet's data
*/
public RawPacket(byte[] buffer, int offset, int length)
{
this.buffer = buffer;
this.offset = offset;
this.length = length;
}
/**
* Get buffer containing the content of this packet
*
* @return buffer containing the content of this packet
*/
public byte[] getBuffer()
{
return this.buffer;
}
/**
* Get the length of this packet's data
*
* @return length of this packet's data
*/
public int getLength()
{
return this.length;
}
/**
* Get the start offset of this packet's data inside storing buffer
*
* @return start offset of this packet's data inside storing buffer
*/
public int getOffset()
{
return this.offset;
}
/**
* @param buffer the buffer to set
*/
protected void setBuffer(byte[] buffer) {
this.buffer = buffer;
}
/**
* @param offset the offset to set
*/
protected void setOffset(int offset) {
this.offset = offset;
}
/**
* @param length the length to set
*/
protected void setLength(int length) {
this.length = length;
}
/**
* Sets or resets the marker bit of this packet according to the
* <tt>marker</tt> parameter.
* @param marker <tt>true</tt> if we are to raise the marker bit and
* <tt>false</tt> otherwise.
*/
public void setMarker(boolean marker)
{
if(marker)
{
buffer[offset + 1] |= (byte) 0x80;
}
else
{
buffer[offset + 1] &= (byte) 0x7F;
}
}
/**
* Sets the payload of this packet.
*
* @param payload the RTP payload type describing the content of this
* packet.
*/
public void setPayload(byte payload)
{
//this is supposed to be a 7bit payload so make sure that the leftmost
//bit is 0 so that we don't accidentally overwrite the marker.
payload &= (byte)0x7F;
buffer[offset + 1] = (byte)((buffer[offset + 1] & 0x80) | payload);
}
/**
* Returns the timestamp for this RTP <tt>RawPacket</tt>.
*
* @return the timestamp for this RTP <tt>RawPacket</tt>.
*/
public long getTimestamp()
{
return readInt(offset + 4);
}
/**
* Set the timestamp value of the RTP Packet
*
* @param timestamp : the RTP Timestamp
*/
public void setTimestamp(long timestamp)
{
writeInt(offset + 4, (int)timestamp);
}
/**
* Read a integer from this packet at specified offset
*
* @param off start offset of the integer to be read
* @return the integer to be read
*/
public int readInt(int off)
{
return (this.buffer[this.offset + off + 0] << 24) |
((this.buffer[this.offset + off + 1] & 0xff) << 16) |
((this.buffer[this.offset + off + 2] & 0xff) << 8) |
(this.buffer[this.offset + off + 3] & 0xff);
}
/**
* Set an integer at specified offset in network order.
*
* @param off Offset into the buffer
* @param data The integer to store in the packet
*/
public void writeInt(int off, int data)
{
buffer[offset + off++] = (byte)(data>>24);
buffer[offset + off++] = (byte)(data>>16);
buffer[offset + off++] = (byte)(data>>8);
buffer[offset + off] = (byte)data;
}
/**
* Read a short from this packet at specified offset
*
* @param off start offset of this short
* @return short value at offset
*/
public short readShort(int off)
{
return (short) ((this.buffer[this.offset + off + 0] << 8) |
(this.buffer[this.offset + off + 1] & 0xff));
}
/**
* Read an unsigned short at specified offset as a int
*
* @param off start offset of the unsigned short
* @return the int value of the unsigned short at offset
*/
public int readUnsignedShortAsInt(int off)
{
int b1 = (0x000000FF & (this.buffer[this.offset + off + 0]));
int b2 = (0x000000FF & (this.buffer[this.offset + off + 1]));
int val = b1 << 8 | b2;
return val;
}
/**
* Read a byte from this packet at specified offset
*
* @param off start offset of the byte
* @return byte at offset
*/
public byte readByte(int off)
{
return buffer[offset + off];
}
/**
* Write a byte to this packet at specified offset
*
* @param off start offset of the byte
* @param b byte to write
*/
public void writeByte(int off, byte b)
{
buffer[offset + off] = b;
}
/**
* Read an unsigned integer as long at specified offset
*
* @param off start offset of this unsigned integer
* @return unsigned integer as long at offset
*/
public long readUnsignedIntAsLong(int off)
{
int b0 = (0x000000FF & (this.buffer[this.offset + off + 0]));
int b1 = (0x000000FF & (this.buffer[this.offset + off + 1]));
int b2 = (0x000000FF & (this.buffer[this.offset + off + 2]));
int b3 = (0x000000FF & (this.buffer[this.offset + off + 3]));
return ((b0 << 24 | b1 << 16 | b2 << 8 | b3)) & 0xFFFFFFFFL;
}
/**
* Read a byte region from specified offset with specified length
*
* @param off start offset of the region to be read
* @param len length of the region to be read
* @return byte array of [offset, offset + length)
*/
public byte[] readRegion(int off, int len)
{
int startOffset = this.offset + off;
if (off < 0 || len <= 0 || startOffset + len > this.buffer.length)
return null;
byte[] region = new byte[len];
System.arraycopy(this.buffer, startOffset, region, 0, len);
return region;
}
/**
* Read a byte region from specified offset with specified length in given
* buffer
*
* @param off start offset of the region to be read
* @param len length of the region to be read
* @param outBuff output buffer
*/
public void readRegionToBuff(int off, int len, byte[] outBuff)
{
int startOffset = this.offset + off;
if (off < 0 || len <= 0 || startOffset + len > this.buffer.length)
return;
if (outBuff.length < len)
return;
System.arraycopy(this.buffer, startOffset, outBuff, 0, len);
}
/**
* Grow the internal packet buffer.
*
* This will change the data buffer of this packet but not the
* length of the valid data. Use this to grow the internal buffer
* to avoid buffer re-allocations when appending data.
*
* @param howMuch number of bytes to grow
*/
public void grow(int howMuch) {
if (howMuch == 0) {
return;
}
byte[] newBuffer = new byte[this.length + howMuch];
System.arraycopy(this.buffer, this.offset, newBuffer, 0, this.length);
offset = 0;
buffer = newBuffer;
}
/**
* Append a byte array to the end of the packet. This may change the data
* buffer of this packet.
*
* @param data byte array to append
* @param len the number of bytes to append
*/
public void append(byte[] data, int len) {
if (data == null || len == 0) {
return;
}
// re-allocate internal buffer if it is too small
if ((this.length + len) > (buffer.length - this.offset)) {
byte[] newBuffer = new byte[this.length + len];
System.arraycopy(this.buffer, this.offset, newBuffer, 0, this.length);
this.offset = 0;
this.buffer = newBuffer;
}
// append data
System.arraycopy(data, 0, this.buffer, this.length, len);
this.length = this.length + len;
}
/**
* Shrink the buffer of this packet by specified length
*
* @param len length to shrink
*/
public void shrink(int len)
{
if (len <= 0)
return;
this.length -= len;
if (this.length < 0)
this.length = 0;
}
/**
* Returns the number of CSRC identifiers currently included in this packet.
*
* @return the CSRC count for this <tt>RawPacket</tt>.
*/
public int getCsrcCount()
{
return (buffer[offset] & 0x0f);
}
/**
* Replaces the existing CSRC list (even if empty) with <tt>newCsrcList</tt>
* and updates the CC (CSRC count) field of this <tt>RawPacket</tt>
* accordingly.
*
* @param newCsrcList the list of CSRC identifiers that we'd like to set for
* this <tt>RawPacket</tt>.
*/
public void setCsrcList(long[] newCsrcList)
{
int newCsrcCount = newCsrcList.length;
byte[] csrcBuff = new byte[newCsrcCount * 4];
int csrcOffset = 0;
for(long csrc : newCsrcList)
{
csrcBuff[csrcOffset] = (byte)(csrc >> 24);
csrcBuff[csrcOffset+1] = (byte)(csrc >> 16);
csrcBuff[csrcOffset+2] = (byte)(csrc >> 8);
csrcBuff[csrcOffset+3] = (byte)csrc;
csrcOffset += 4;
}
int oldCsrcCount = getCsrcCount();
byte[] oldBuffer = this.getBuffer();
//the new buffer needs to be bigger than the new one in order to
//accommodate the list of CSRC IDs (unless there were more of them
//previously than after setting the new list).
byte[] newBuffer
= new byte[length + offset + csrcBuff.length - oldCsrcCount*4];
//copy the part up to the CSRC list
System.arraycopy(
oldBuffer, 0, newBuffer, 0, offset + FIXED_HEADER_SIZE);
//copy the new CSRC list
System.arraycopy( csrcBuff, 0, newBuffer,
offset + FIXED_HEADER_SIZE, csrcBuff.length);
//now copy the payload from the old buff and make sure we don't copy
//the CSRC list if there was one in the old packet
int payloadOffsetForOldBuff
= offset + FIXED_HEADER_SIZE + oldCsrcCount*4;
int payloadOffsetForNewBuff
= offset + FIXED_HEADER_SIZE + newCsrcCount*4;
System.arraycopy( oldBuffer, payloadOffsetForOldBuff,
newBuffer, payloadOffsetForNewBuff,
length - payloadOffsetForOldBuff);
//set the new CSRC count
newBuffer[offset] = (byte)((newBuffer[offset] & 0xF0)
| newCsrcCount);
this.buffer = newBuffer;
this.length = payloadOffsetForNewBuff + length
- payloadOffsetForOldBuff - offset;
}
/**
* Returns the list of CSRC IDs, currently encapsulated in this packet.
*
* @return an array containing the list of CSRC IDs, currently encapsulated
* in this packet.
*/
public long[] extractCsrcList()
{
int csrcCount = getCsrcCount();
long[] csrcList = new long[csrcCount];
int csrcStartIndex = offset + FIXED_HEADER_SIZE;
for (int i = 0; i < csrcCount; i++)
{
csrcList[i] = readInt(csrcStartIndex);
csrcStartIndex += 4;
}
return csrcList;
}
/**
* Get RTP padding size from a RTP packet
*
* @return RTP padding size from source RTP packet
*/
public int getPaddingSize()
{
if ((buffer[offset] & 0x4) == 0)
return 0;
else
return buffer[offset + length - 1];
}
/**
* Get RTP header length from a RTP packet
*
* @return RTP header length from source RTP packet
*/
public int getHeaderLength()
{
if(getExtensionBit())
return FIXED_HEADER_SIZE + 4 * getCsrcCount()
+ EXT_HEADER_SIZE + getExtensionLength();
else
return FIXED_HEADER_SIZE + 4 * getCsrcCount();
}
/**
* Get RTP payload length from a RTP packet
*
* @return RTP payload length from source RTP packet
*/
public int getPayloadLength()
{
return length - getHeaderLength();
}
/**
* Get RTP SSRC from a RTP packet
*
* @return RTP SSRC from source RTP packet
*/
public int getSSRC()
{
return (int)(readUnsignedIntAsLong(8) & 0xffffffff);
}
/**
* Get RTCP SSRC from a RTCP packet
*
* @return RTP SSRC from source RTP packet
*/
public long getRTCPSSRC()
{
return (int)(readUnsignedIntAsLong(4) & 0xffffffff);
}
/**
* Get RTP sequence number from a RTP packet
*
* @return RTP sequence num from source packet
*/
public int getSequenceNumber()
{
return readUnsignedShortAsInt(2);
}
/**
* Get SRTCP sequence number from a SRTCP packet
*
* @param authTagLen authentication tag length
* @return SRTCP sequence num from source packet
*/
public int getSRTCPIndex(int authTagLen)
{
int offset = getLength() - (4 + authTagLen);
return readInt(offset);
}
/**
* Test whether if a RTP packet is padded
*
* @return whether if source RTP packet is padded
*/
public boolean isPacketMarked()
{
return (buffer[offset + 1] & 0x80) != 0;
}
/**
* Get RTP payload type from a RTP packet
*
* @return RTP payload type of source RTP packet
*/
public byte getPayloadType()
{
return (byte) (buffer[offset + 1] & (byte)0x7F);
}
/**
* Get the RTP payload (bytes) of this RTP packet.
*
* @return an array of <tt>byte</tt>s which represents the RTP payload of
* this RTP packet
*/
public byte[] getPayload()
{
return readRegion(getHeaderLength(), getPayloadLength());
}
/**
* Get RTP timestamp from a RTP packet
*
* @return RTP timestamp of source RTP packet
*/
public byte[] readTimeStampIntoByteArray()
{
return readRegion(4, 4);
}
/**
* Returns <tt>true</tt> if the extension bit of this packet has been set
* and false otherwise.
*
* @return <tt>true</tt> if the extension bit of this packet has been set
* and false otherwise.
*/
public boolean getExtensionBit()
{
return (buffer[offset] & 0x10) == 0x10;
}
/**
* Raises the extension bit of this packet is <tt>extBit</tt> is
* <tt>true</tt> or set it to <tt>0</tt> if <tt>extBit</tt> is
* <tt>false</tt>.
*
* @param extBit the flag that indicates whether we are to set or clear
* the extension bit of this packet.
*/
private void setExtensionBit(boolean extBit)
{
if(extBit)
buffer[offset] |= 0x10;
else
buffer[offset] &= 0xEF;
}
/**
* Returns the length of the extensions currently added to this packet.
*
* @return the length of the extensions currently added to this packet.
*/
public int getExtensionLength()
{
if (!getExtensionBit())
return 0;

Lyubomir Marinov
committed
// The extension length comes after the RTP header, the CSRC list, and
// two bytes in the extension header called "defined by profile".
int extLenIndex = offset + FIXED_HEADER_SIZE + getCsrcCount() * 4 + 2;

Lyubomir Marinov
committed
return
((buffer[extLenIndex] << 8) | (buffer[extLenIndex + 1] & 0xFF)) * 4;
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
}
/**
* Adds the <tt>extBuff</tt> buffer to as an extension of this packet
* according the rules specified in RFC 5285. Note that this method does
* not replace extensions so if you add the same buffer twice it would be
* added as to separate extensions.
*
* @param extBuff the buffer that we'd like to add as an extension in this
* packet.
* @param newExtensionLen the length of the data in extBuff.
*/
public void addExtension(byte[] extBuff, int newExtensionLen)
{
int newBuffLen = length + offset + newExtensionLen;
int bufferOffset = offset;
int newBufferOffset = offset;
int lengthToCopy = FIXED_HEADER_SIZE + getCsrcCount()*4;
boolean extensionBit = getExtensionBit();
//if there was no extension previously, we also need to consider adding
//the extension header.
if (extensionBit)
{
// without copying the extension length value, will set it later
lengthToCopy += EXT_HEADER_SIZE - 2;
}
else
newBuffLen += EXT_HEADER_SIZE;
byte[] newBuffer = new byte[ newBuffLen ];
/*
* Copy header, CSRC list and the leading two bytes of the extension
* header if any.
*/
System.arraycopy(buffer, bufferOffset,
newBuffer, newBufferOffset, lengthToCopy);
//raise the extension bit.
newBuffer[newBufferOffset] |= 0x10;
bufferOffset += lengthToCopy;
newBufferOffset += lengthToCopy;
// Set the extension header or modify the existing one.
int totalExtensionLen = newExtensionLen + getExtensionLength();
//if there were no extensions previously, we need to add the hdr now
if(extensionBit)
bufferOffset += 4;
else
{
// we will now be adding the RFC 5285 ext header which looks like
// this:
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | 0xBE | 0xDE | length=3 |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
newBuffer[newBufferOffset++] = (byte)0xBE;
newBuffer[newBufferOffset++] = (byte)0xDE;
}
// length field counts the number of 32-bit words in the extension
int lengthInWords = (totalExtensionLen + 3)/4;
newBuffer[newBufferOffset++] = (byte)(lengthInWords >> 8);
newBuffer[newBufferOffset++] = (byte)lengthInWords;
// Copy the existing extension content if any.
if (extensionBit)
{
lengthToCopy = getExtensionLength();
System.arraycopy(buffer, bufferOffset,
newBuffer, newBufferOffset, lengthToCopy);
bufferOffset += lengthToCopy;
newBufferOffset += lengthToCopy;
}
//copy the extension content from the new extension.
System.arraycopy(extBuff, 0,
newBuffer, newBufferOffset, newExtensionLen);
newBufferOffset += newExtensionLen;
//now copy the payload
System.arraycopy(buffer, bufferOffset,
newBuffer, newBufferOffset, getPayloadLength());
newBufferOffset += getPayloadLength();
buffer = newBuffer;
this.length = newBufferOffset - offset;
}
/**
* Removes the extension from the packet and its header.
*/
public void removeExtension()
{
if(!getExtensionBit())
return;
int payloadOffset = offset + getHeaderLength();
int extHeaderLen = getExtensionLength() + EXT_HEADER_SIZE;
System.arraycopy(buffer, payloadOffset,
buffer, payloadOffset - extHeaderLen, getPayloadLength());
this.length -= extHeaderLen;
setExtensionBit(false);
}
/**
* Returns a map binding CSRC IDs to audio levels as reported by the remote
* party that sent this packet.
*
* @param csrcExtID the ID of the extension that's transporting csrc audio
* levels in the session that this <tt>RawPacket</tt> belongs to.
*
* @return an array representing a map binding CSRC IDs to audio levels as
* reported by the remote party that sent this packet. The entries of the
* map are contained in consecutive elements of the returned array where
* elements at even indices stand for CSRC IDs and elements at odd indices
* stand for the associated audio levels
*/
public long[] extractCsrcLevels(byte csrcExtID)
{
if (!getExtensionBit()
|| (getExtensionLength() == 0)
|| (getCsrcCount() == 0))
return null;
int csrcCount = getCsrcCount();
/*
* XXX The guideline which is also supported by Google and recommended
* for Android is that single-dimensional arrays should be preferred to
* multi-dimensional arrays in Java because the former take less space
* than the latter and are thus more efficient in terms of memory and
* garbage collection.
*/
long[] csrcLevels = new long[csrcCount * 2];
//first extract the csrc IDs
int csrcStartIndex = offset + FIXED_HEADER_SIZE;
for (int i = 0; i < csrcCount; i++)
{
int csrcLevelsIndex = 2 * i;
csrcLevels[csrcLevelsIndex] = readInt(csrcStartIndex);
csrcLevels[csrcLevelsIndex + 1] = getCsrcLevel(i, csrcExtID);
csrcStartIndex += 4;
}
return csrcLevels;
}
/**
* Returns the CSRC level at the specified index or <tt>0</tt> if there was
* no level at that index.
*
* @param index the sequence number of the CSRC audio level extension to
* return.
* @param csrcExtID the ID of the extension that's transporting csrc audio
* levels in the session that this <tt>RawPacket</tt> belongs to.
*
* @return the CSRC audio level at the specified index of the csrc audio
* level option or <tt>0</tt> if there was no level at that index.
*/
private int getCsrcLevel(int index, byte csrcExtID)
{
if( !getExtensionBit() || getExtensionLength() == 0)
return 0;
int levelsStart = findExtension(csrcExtID);
if(levelsStart == -1)
return 0;
int levelsCount = getLengthForExtension(levelsStart);
if(levelsCount < index)
{
//apparently the remote side sent more CSRCs than levels.
// ... yeah remote sides do that now and then ...
return 0;
}
return buffer[levelsStart + index];
}
/**
* Returns the index of the element in this packet's buffer where the
* content of the header with the specified <tt>extensionID</tt> starts.
*
* @param extensionID the ID of the extension whose content we are looking
* for.
*
* @return the index of the first byte of the content of the extension
* with the specified <tt>extensionID</tt> or -1 if no such extension was
* found.
*/
private int findExtension(int extensionID)
{
if( !getExtensionBit() || getExtensionLength() == 0)
return 0;
int extOffset = offset + FIXED_HEADER_SIZE
+ getCsrcCount()*4 + EXT_HEADER_SIZE;
int extensionEnd = extOffset + getExtensionLength();
int extHdrLen = getExtensionHeaderLength();
if (extHdrLen != 1 && extHdrLen != 2)
{
return -1;
}
while (extOffset < extensionEnd)
{
int currType = -1;
int currLen = -1;
if(extHdrLen == 1)
{
//short header. type is in the lefter 4 bits and length is on
//the right; like this:
// 0
// 0 1 2 3 4 5 6 7
// +-+-+-+-+-+-+-+-+
// | ID | len |
// +-+-+-+-+-+-+-+-+
currType = buffer[extOffset] >> 4;
currLen = (buffer[extOffset] & 0x0F) + 1; //add one as per 5285
//now skip the header
extOffset ++;
}
else
{
//long header. type is in the first byte and length is in the
//second
// 0 1
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
currType = buffer[extOffset];
currLen = buffer[extOffset + 1];
//now skip the header
extOffset += 2;
}
if(currType == extensionID)
{
return extOffset;
}
extOffset += currLen;
}
return -1;
}
/**
* Returns the length of the header extension that is carrying the content
* starting at <tt>contentStart</tt>. In other words this method checks the
* size of extension headers in this packet and then either returns the
* value of the byte right before <tt>contentStart</tt> or its lower 4 bits.
* This is a very basic method so if you are using it - make sure u know
* what you are doing.
*
* @param contentStart the index of the first element of the content of
* the extension whose size we are trying to obtain.
*
* @return the length of the extension carrying the content starting at
* <tt>contentStart</tt>.
*/
private int getLengthForExtension(int contentStart)
{
int hdrLen = getExtensionHeaderLength();
if( hdrLen == 1 )
return ( buffer[contentStart - 1] & 0x0F ) + 1;
else
return buffer[contentStart - 1];
}
/**
* Returns the length of the extension header being used in this packet or
* <tt>-1</tt> in case there were no extension headers here or we didn't
* understand the kind of extension being used.
*
* @return the length of the extension header being used in this packet or
* <tt>-1</tt> in case there were no extension headers here or we didn't
* understand the kind of extension being used.
*/
private int getExtensionHeaderLength()
{
if (!getExtensionBit())
return -1;
//the type of the extension header comes right after the RTP header and
//the CSRC list.
int extLenIndex = offset + FIXED_HEADER_SIZE + getCsrcCount()*4;
//0xBEDE means short extension header.
if (buffer[extLenIndex] == (byte)0xBE
&& buffer[extLenIndex + 1] == (byte)0xDE)
return 1;
//0x100 means a two-byte extension header.
if (buffer[extLenIndex]== (byte)0x10
&& (buffer[extLenIndex + 1] >> 4)== 0)
return 2;
return -1;
}
/**
* Return the define by profile part of the extension header.
* @return the starting two bytes of extension header.
*/
public int getHeaderExtensionType()
{
if (!getExtensionBit())
return 0;
return readUnsignedShortAsInt(
offset + FIXED_HEADER_SIZE + getCsrcCount()*4);
}
}