/**
     * Updates this <tt>RTPStatsMap</tt> with information it gets from the <tt>RawPacket</tt>.
     *
     * @param pkt the <tt>RawPacket</tt> that is being transmitted.
     */
    public void apply(RawPacket pkt) {
      int ssrc = pkt.getSSRC();
      if (this.containsKey(ssrc)) {
        RTPStatsEntry oldRtpStatsEntry = this.get(ssrc);

        // Replace whatever was in there before. A feature of the two's
        // complement encoding (which is used by Java integers) is that
        // the bitwise results for add, subtract, and multiply are the
        // same if both inputs are interpreted as signed values or both
        // inputs are interpreted as unsigned values. (Other encodings
        // like one's complement and signed magnitude don't have this
        // properly.)
        this.put(
            ssrc,
            new RTPStatsEntry(
                ssrc,
                oldRtpStatsEntry.getBytesSent()
                    + pkt.getLength()
                    - pkt.getHeaderLength()
                    - pkt.getPaddingSize(),
                oldRtpStatsEntry.getPacketsSent() + 1));
      } else {
        // Add a new <tt>RTPStatsEntry</tt> in this map.
        this.put(
            ssrc,
            new RTPStatsEntry(
                ssrc, pkt.getLength() - pkt.getHeaderLength() - pkt.getPaddingSize(), 1));
      }
    }
    /**
     * Updates octet count and packet count in sender reports found in the
     * <tt>RTCPCompoundPacket</tt>.
     *
     * @param pkt
     * @return {@code true} if the specified {@code pkt} was modified; otherwise, {@code false}
     */
    public boolean gateway(RTCPCompoundPacket pkt) {
      RTCPPacket[] pkts;
      boolean modified = false;

      if (pkt != null && (pkts = pkt.packets) != null && pkts.length != 0) {
        for (RTCPPacket p : pkts) {
          switch (p.type) {
            case RTCPPacket.SR:
              RTCPSRPacket sr = (RTCPSRPacket) p;
              int ssrc = sr.ssrc;
              RTPStatsEntry rtpStats = rtpStatsMap.get(ssrc);

              if (rtpStats != null) {
                // Mark the packet as modified and update the octet
                // and packet count using the information gathered
                // by rtpStatsMap.
                sr.octetcount = rtpStats.getBytesSent();
                sr.packetcount = rtpStats.getPacketsSent();
                modified = true;
              }
              break;
          }
        }
      }
      return modified;
    }
  /**
   * Makes <tt>RTCPSRPacket</tt>s for all the RTP streams that we're sending.
   *
   * @return a <tt>List</tt> of <tt>RTCPSRPacket</tt> for all the RTP streams that we're sending.
   */
  private Collection<RTCPSRPacket> makeRTCPSRPackets(long time) {
    Collection<RTCPSRPacket> srPackets = new ArrayList<RTCPSRPacket>();

    for (RTPStatsEntry rtpStatsEntry : rtpStatsMap.values()) {
      int ssrc = rtpStatsEntry.getSsrc();
      RemoteClock estimate = remoteClockEstimator.estimate(ssrc, time);
      if (estimate == null) {
        // We're not going to go far without an estimate..
        continue;
      }

      RTCPSRPacket srPacket = new RTCPSRPacket(ssrc, MIN_RTCP_REPORTS_BLOCKS_ARRAY);

      // Set the NTP timestamp for this SR.
      long estimatedRemoteTime = estimate.getRemoteTime();
      long secs = estimatedRemoteTime / 1000L;
      double fraction = (estimatedRemoteTime - secs * 1000L) / 1000D;
      srPacket.ntptimestamplsw = (int) (fraction * 4294967296D);
      srPacket.ntptimestampmsw = secs;

      // Set the RTP timestamp.
      srPacket.rtptimestamp = estimate.getRtpTimestamp();

      // Fill-in packet and octet send count.
      srPacket.packetcount = rtpStatsEntry.getPacketsSent();
      srPacket.octetcount = rtpStatsEntry.getBytesSent();

      srPackets.add(srPacket);
    }

    return srPackets;
  }