/**
     * Removes receiver and sender feedback from RTCP packets.
     *
     * @param inPacket the <tt>RTCPCompoundPacket</tt> to filter.
     * @return the filtered <tt>RawPacket</tt>.
     */
    public RawPacket gateway(RTCPCompoundPacket inPacket) {
      if (inPacket == null || inPacket.packets == null || inPacket.packets.length == 0) {
        logger.info("Ignoring empty RTCP packet.");
        return null;
      }

      ArrayList<RTCPPacket> outPackets = new ArrayList<RTCPPacket>(inPacket.packets.length);

      for (RTCPPacket p : inPacket.packets) {
        switch (p.type) {
          case RTCPPacket.RR:
          case RTCPPacket.SR:
          case RTCPPacket.SDES:
            // We generate our own RR/SR/SDES packets. We only want
            // to forward NACKs/PLIs/etc.
            break;
          case RTCPFBPacket.PSFB:
            RTCPFBPacket psfb = (RTCPFBPacket) p;
            switch (psfb.fmt) {
              case RTCPREMBPacket.FMT:
                // We generate its own REMB packets.
                break;
              default:
                // We let through everything else, like NACK
                // packets.
                outPackets.add(psfb);
                break;
            }
            break;
          default:
            // We let through everything else, like BYE and APP
            // packets.
            outPackets.add(p);
            break;
        }
      }

      if (outPackets.size() == 0) {
        return null;
      }

      // We have feedback messages to send. Pack them in a compound
      // RR and send them. TODO Use RFC5506 Reduced-Size RTCP, if the
      // receiver supports it.
      Collection<RTCPRRPacket> rrPackets = makeRTCPRRPackets(System.currentTimeMillis());

      if (rrPackets != null && rrPackets.size() != 0) {
        outPackets.addAll(0, rrPackets);
      } else {
        logger.warn("We might be sending invalid RTCPs.");
      }

      RTCPPacket[] pkts = outPackets.toArray(new RTCPPacket[outPackets.size()]);
      RTCPCompoundPacket outPacket = new RTCPCompoundPacket(pkts);

      return generator.apply(outPacket);
    }
 /**
  * Ctor.
  *
  * @param ssrc
  * @param remoteTime
  * @param rtpTimestamp
  * @param frequencyHz
  */
 ReceivedRemoteClock(int ssrc, long remoteTime, int rtpTimestamp, int frequencyHz) {
   this.ssrc = ssrc;
   this.remoteClock = new RemoteClock(remoteTime, rtpTimestamp);
   this.frequencyHz = frequencyHz;
   this.receivedTime = System.currentTimeMillis();
 }
  /** {@inheritDoc} */
  @Override
  public RawPacket report() {
    garbageCollector.cleanup();

    // TODO Compound RTCP packets should not exceed the MTU of the network
    // path.
    //
    // An individual RTP participant should send only one compound RTCP
    // packet per report interval in order for the RTCP bandwidth per
    // participant to be estimated correctly, except when the compound
    // RTCP packet is split for partial encryption.
    //
    // If there are too many sources to fit all the necessary RR packets
    // into one compound RTCP packet without exceeding the maximum
    // transmission unit (MTU) of the network path, then only the subset
    // that will fit into one MTU should be included in each interval. The
    // subsets should be selected round-robin across multiple intervals so
    // that all sources are reported.
    //
    // It is impossible to know in advance what the MTU of path will be.
    // There are various algorithms for experimenting to find out, but many
    // devices do not properly implement (or deliberately ignore) the
    // necessary standards so it all comes down to trial and error. For that
    // reason, we can just guess 1200 or 1500 bytes per message.
    long time = System.currentTimeMillis();

    Collection<RTCPPacket> packets = new ArrayList<RTCPPacket>();

    // First, we build the RRs.
    Collection<RTCPRRPacket> rrPackets = makeRTCPRRPackets(time);
    if (rrPackets != null && rrPackets.size() != 0) {
      packets.addAll(rrPackets);
    }

    // Next, we build the SRs.
    Collection<RTCPSRPacket> srPackets = makeRTCPSRPackets(time);
    if (srPackets != null && srPackets.size() != 0) {
      packets.addAll(srPackets);
    }

    // Bail out if we have nothing to report.
    if (packets.size() == 0) {
      return null;
    }

    // Next, we build the REMB.
    RTCPREMBPacket rembPacket = makeRTCPREMBPacket();
    if (rembPacket != null) {
      packets.add(rembPacket);
    }

    // Finally, we add an SDES packet.
    RTCPSDESPacket sdesPacket = makeSDESPacket();
    if (sdesPacket != null) {
      packets.add(sdesPacket);
    }

    // Prepare the <tt>RTCPCompoundPacket</tt> to return.
    RTCPPacket rtcpPackets[] = packets.toArray(new RTCPPacket[packets.size()]);

    RTCPCompoundPacket cp = new RTCPCompoundPacket(rtcpPackets);

    // Build the <tt>RTCPCompoundPacket</tt> and return the
    // <tt>RawPacket</tt> to inject to the <tt>MediaStream</tt>.
    return generator.apply(cp);
  }