gt:         if (buffer[extLenIndex] == (byte)0xBE
context: /*
   * 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
   * @author Boris Grozev
   * @author Lyubomir Marinov
   */
  public class RawPacket
  {
      /**
       * The size of the extension header as defined by RFC 3550.
       */
      public static final int EXT_HEADER_SIZE = 4;
  
      /**
       * The size of the fixed part of the RTP header as defined by RFC 3550.
       */
      public static final int FIXED_HEADER_SIZE = 12;
  
      /**
       * Byte array storing the content of this Packet
       */
      private byte[] buffer;
  
      /**
       * The bitmap/flag mask that specifies the set of boolean attributes enabled
       * for this <tt>RawPacket</tt>. The value is the logical sum of all of the
       * set flags. The possible flags are defined by the <tt>FLAG_XXX</tt>
       * constants of FMJ's {@link Buffer} class.
       */
      private int flags;
  
      /**
       * Length of this packet's data
       */
      private int length;
  
      /**
       * 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;
  
      /**
       * Initializes a new empty <tt>RawPacket</tt> instance.
       */
      public RawPacket()
      {
      }
  
      /**
       * Initializes a new <tt>RawPacket</tt> instance with a specific
       * <tt>byte</tt> array buffer.
       *
       * @param buffer the <tt>byte</tt> array to be the buffer of the new
       * instance 
       * @param offset the offset in <tt>buffer</tt> at which the actual data to
       * be represented by the new instance starts
       * @param length the number of <tt>byte</tt>s in <tt>buffer</tt> which
       * constitute the actual data to be represented by the new instance
       */
      public RawPacket(byte[] buffer, int offset, int length)
      {
          this.buffer = buffer;
          this.offset = offset;
          this.length = length;
      }
  
      /**
       * Adds the <tt>extBuff</tt> buffer 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 a separate extension.
       *
       * @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)
          {
              // We've copied "defined by profile" already. Consequently, we have
              // to skip the length only.
              bufferOffset += 2;
          }
          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
          int payloadLength = getPayloadLength();
  
          System.arraycopy(
                  buffer, bufferOffset,
                  newBuffer, newBufferOffset,
                  payloadLength);
          newBufferOffset += payloadLength;
  
          buffer = newBuffer;
          this.length = newBufferOffset - offset;
      }
  
      /**
       * 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;
  
      }
  
      /**
       * 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[] extractCsrcAudioLevels(byte csrcExtID)
      {
          if (!getExtensionBit() || (getExtensionLength() == 0))
              return null;
  
          int csrcCount = getCsrcCount();
  
          if (csrcCount == 0)
              return null;
  
          /*
           * 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
          for (int i = 0, csrcStartIndex = offset + FIXED_HEADER_SIZE;
                  i < csrcCount;
                  i++, csrcStartIndex += 4)
          {
              int csrcLevelsIndex = 2 * i;
  
              csrcLevels[csrcLevelsIndex] = 0xFFFFFFFFL & readInt(csrcStartIndex);
              /*
               * The audio levels generated by Jitsi are not in accord with the
               * respective specification, they are backwards with respect to the
               * value domain. Which means that the audio level generated from a
               * muted audio source is 0/zero.
               */
              csrcLevels[csrcLevelsIndex + 1]
                  = getCsrcAudioLevel(csrcExtID, i, (byte) 0);
          }
  
          return csrcLevels;
      }
  
      /**
       * 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];
  
          for (int i = 0, csrcStartIndex = offset + FIXED_HEADER_SIZE;
                  i < csrcCount;
                  i++, csrcStartIndex += 4)
          {
              csrcList[i] = readInt(csrcStartIndex);
          }
  
          return csrcList;
      }
  
      /**
       * Extracts the source audio level reported by the remote party which sent
       * this packet and carried in this packet.
       *
       * @param ssrcExtID the ID of the extension that's transporting ssrc audio
       * levels in the session that this <tt>RawPacket</tt> belongs to
       * @return the source audio level reported by the remote party which sent
       * this packet and carried in this packet or a negative value if this packet
       * contains no extension such as the specified by <tt>ssrcExtID</tt>
       */
      public byte extractSsrcAudioLevel(byte ssrcExtID)
      {
          /*
           * The method getCsrcAudioLevel(byte, int) is implemented with the
           * awareness that there may be a flag bit V with a value other than 0.
           */
          /*
           * The audio levels sent by Google Chrome are in accord with the
           * specification i.e. the audio level generated from a muted audio
           * source is 127 and the values are non-negative. If there is no source
           * audio level in this packet, return a negative value.
           */
          return getCsrcAudioLevel(ssrcExtID, 0, Byte.MIN_VALUE);
      }
  
      /**
       * 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;
      }
  
      /**
       * Get buffer containing the content of this packet
       *
       * @return buffer containing the content of this packet
       */
      public byte[] getBuffer()
      {
          return this.buffer;
      }
  
  
      /**
       * Returns the CSRC level at the specified index or <tt>0</tt> if there was
       * no level at that index.
       *
       * @param csrcExtID the ID of the extension that's transporting csrc audio
       * levels in the session that this <tt>RawPacket</tt> belongs to.
       * @param index the sequence number of the CSRC audio level extension to
       * return.
       *
       * @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 byte getCsrcAudioLevel(byte csrcExtID, int index, byte defaultValue)
      {
          byte level = defaultValue;
  
          if (getExtensionBit() && getExtensionLength() != 0)
          {
              int levelsStart = findExtension(csrcExtID);
  
              if (levelsStart != -1)
              {
                  int levelsCount = getLengthForExtension(levelsStart);
  
                  if (levelsCount < index)
                  {
                      //apparently the remote side sent more CSRCs than levels.
                      // ... yeah remote sides do that now and then ...
                  }
                  else
                  {
                      level = (byte) (0x7F & buffer[levelsStart + index]);
                  }
              }
          }
          return level;
      }
  
      /**
       * 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);
      }
  
      /**
       * Returns <tt>true</tt> if the extension bit of this packet has been set
       * and <tt>false</tt> otherwise.
       *
       * @return  <tt>true</tt> if the extension bit of this packet has been set
       * and <tt>false</tt> otherwise.
       */
      public boolean getExtensionBit()
      {
          return (buffer[offset] & 0x10) == 0x10;
      }
  
      /**
       * 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.
  