/**
     * 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));
      }
    }
Ejemplo n.º 2
0
  /**
   * Removes the RTX encapsulation from a packet.
   *
   * @param pkt the packet to remove the RTX encapsulation from.
   * @return the original media packet represented by {@code pkt}, or null if we couldn't
   *     reconstruct the original packet.
   */
  private RawPacket deRtx(RawPacket pkt) {
    boolean success = false;

    if (pkt.getPayloadLength() - pkt.getPaddingSize() < 2) {
      // We need at least 2 bytes to read the OSN field.
      if (logger.isDebugEnabled()) {
        logger.debug("Dropping an incoming RTX packet with padding only: " + pkt);
      }
      return null;
    }

    long mediaSsrc = getPrimarySsrc(pkt);
    if (mediaSsrc != -1) {
      if (rtxAssociatedPayloadType != -1) {
        int osn = pkt.getOriginalSequenceNumber();
        // Remove the RTX header by moving the RTP header two bytes
        // right.
        byte[] buf = pkt.getBuffer();
        int off = pkt.getOffset();
        System.arraycopy(buf, off, buf, off + 2, pkt.getHeaderLength());

        pkt.setOffset(off + 2);
        pkt.setLength(pkt.getLength() - 2);

        pkt.setSSRC((int) mediaSsrc);
        pkt.setSequenceNumber(osn);
        pkt.setPayloadType(rtxAssociatedPayloadType);
        success = true;
      } else {
        logger.warn(
            "RTX packet received, but no APT is defined. Packet "
                + "SSRC "
                + pkt.getSSRCAsLong()
                + ", associated media"
                + " SSRC "
                + mediaSsrc);
      }
    }

    // If we failed to handle the RTX packet, drop it.
    return success ? pkt : null;
  }
Ejemplo n.º 3
0
  /**
   * Encapsulates {@code pkt} in the RTX format, using {@code rtxSsrc} as its SSRC, and transmits it
   * to {@link #channel} by injecting it in the {@code MediaStream}.
   *
   * @param pkt the packet to transmit.
   * @param rtxSsrc the SSRC for the RTX stream.
   * @return {@code true} if the packet was successfully retransmitted, {@code false} otherwise.
   */
  private boolean encapsulateInRtxAndTransmit(RawPacket pkt, long rtxSsrc) {
    byte[] buf = pkt.getBuffer();
    int len = pkt.getLength();
    int off = pkt.getOffset();
    byte[] newBuf = buf;
    if (buf.length < len + 2) {
      // FIXME The byte array newly allocated and assigned to newBuf must
      // be made known to pkt eventually.
      newBuf = new byte[len + 2];
    }

    int osn = pkt.getSequenceNumber();
    int headerLength = pkt.getHeaderLength();
    int payloadLength = len - headerLength;
    System.arraycopy(buf, off, newBuf, 0, headerLength);
    // FIXME If newBuf is actually buf, then we will override the first two
    // bytes of the payload bellow.
    newBuf[headerLength] = (byte) ((osn >> 8) & 0xff);
    newBuf[headerLength + 1] = (byte) (osn & 0xff);
    System.arraycopy(buf, off + headerLength, newBuf, headerLength + 2, payloadLength);
    // FIXME We tried to extend the payload of pkt by two bytes above but
    // we never told pkt that its length has increased by these two bytes.

    MediaStream mediaStream = channel.getStream();
    if (mediaStream != null) {
      pkt.setSSRC((int) rtxSsrc);
      // Only call getNextRtxSequenceNumber() when we're sure we're going
      // to transmit a packet, because it consumes a sequence number.
      pkt.setSequenceNumber(getNextRtxSequenceNumber(rtxSsrc));
      try {
        mediaStream.injectPacket(pkt, /* data */ true, /* after */ null);
      } catch (TransmissionFailedException tfe) {
        logger.warn("Failed to transmit an RTX packet.");
        return false;
      }
    }

    return true;
  }
Ejemplo n.º 4
0
  /**
   * Encapsulates {@code pkt} in the RTX format, using {@code rtxSsrc} as its SSRC, and transmits it
   * to {@link #channel} by injecting it in the {@code MediaStream}.
   *
   * @param pkt the packet to transmit.
   * @param rtxSsrc the SSRC for the RTX stream.
   * @param after the {@code TransformEngine} in the chain of {@code TransformEngine}s of the
   *     associated {@code MediaStream} after which the injection of {@code pkt} is to begin
   * @return {@code true} if the packet was successfully retransmitted, {@code false} otherwise.
   */
  private boolean encapsulateInRtxAndTransmit(RawPacket pkt, long rtxSsrc, TransformEngine after) {
    byte[] buf = pkt.getBuffer();
    int len = pkt.getLength();
    int off = pkt.getOffset();

    byte[] newBuf = new byte[len + 2];
    RawPacket rtxPkt = new RawPacket(newBuf, 0, len + 2);

    int osn = pkt.getSequenceNumber();
    int headerLength = pkt.getHeaderLength();
    int payloadLength = pkt.getPayloadLength();

    // Copy the header.
    System.arraycopy(buf, off, newBuf, 0, headerLength);

    // Set the OSN field.
    newBuf[headerLength] = (byte) ((osn >> 8) & 0xff);
    newBuf[headerLength + 1] = (byte) (osn & 0xff);

    // Copy the payload.
    System.arraycopy(buf, off + headerLength, newBuf, headerLength + 2, payloadLength);

    MediaStream mediaStream = channel.getStream();
    if (mediaStream != null) {
      rtxPkt.setSSRC((int) rtxSsrc);
      rtxPkt.setPayloadType(rtxPayloadType);
      // Only call getNextRtxSequenceNumber() when we're sure we're going
      // to transmit a packet, because it consumes a sequence number.
      rtxPkt.setSequenceNumber(getNextRtxSequenceNumber(rtxSsrc));
      try {
        mediaStream.injectPacket(rtxPkt, /* data */ true, after);
      } catch (TransmissionFailedException tfe) {
        logger.warn("Failed to transmit an RTX packet.");
        return false;
      }
    }

    return true;
  }
Ejemplo n.º 5
0
  /**
   * Creates a {@code RawPacket} which represents the original packet encapsulated in {@code pkt}
   * using the RTX format.
   *
   * @param pkt the packet from which to extract a media packet.
   * @return the extracted media packet.
   */
  private RawPacket createMediaPacket(RawPacket pkt) {
    RawPacket mediaPacket = null;
    long rtxSsrc = pkt.getSSRC() & 0xffffffffL;

    // We need to know the SSRC paired with rtxSsrc *as seen by the
    // receiver (i.e. this.channel)*. However, we only store SSRCs
    // that endpoints *send* with.
    // We therefore assume that SSRC re-writing has not introduced any
    // new SSRCs and therefor the FID mappings known to the senders
    // also apply to receivers.
    RtpChannel sourceChannel = channel.getContent().findChannelByFidSsrc(rtxSsrc);
    if (sourceChannel != null) {
      long mediaSsrc = sourceChannel.getFidPairedSsrc(rtxSsrc);
      if (mediaSsrc != -1) {
        byte apt = sourceChannel.getRtxAssociatedPayloadType();
        if (apt != -1) {
          mediaPacket = new RawPacket(pkt.getBuffer().clone(), pkt.getOffset(), pkt.getLength());

          // Remove the RTX header by moving the RTP header two bytes
          // right.
          byte[] buf = mediaPacket.getBuffer();
          int off = mediaPacket.getOffset();
          System.arraycopy(buf, off, buf, off + 2, mediaPacket.getHeaderLength());

          mediaPacket.setOffset(off + 2);
          mediaPacket.setLength(pkt.getLength() - 2);

          mediaPacket.setSSRC((int) mediaSsrc);
          mediaPacket.setSequenceNumber(pkt.getOriginalSequenceNumber());
          mediaPacket.setPayloadType(apt);
        }
      }
    }

    return mediaPacket;
  }
  /**
   * Notifies this instance that a <tt>DatagramPacket</tt> packet received on the data
   * <tt>DatagramSocket</tt> of this <tt>Channel</tt> has been accepted for further processing
   * within Jitsi Videobridge.
   *
   * @param pkt the accepted <tt>RawPacket</tt>.
   */
  public void accepted(RawPacket pkt) {
    // With native simulcast we don't have a notification when a stream
    // has started/stopped. The simulcast manager implements a timeout
    // for the high quality stream and it needs to be notified when
    // the channel has accepted a datagram packet for the timeout to
    // function correctly.

    if (!hasLayers() || pkt == null) {
      return;
    }

    // Find the layer that corresponds to this packet.
    int acceptedSSRC = pkt.getSSRC();
    SimulcastLayer[] layers = getSimulcastLayers();
    SimulcastLayer acceptedLayer = null;
    for (SimulcastLayer layer : layers) {
      // We only care about the primary SSRC and not the RTX ssrc (or
      // future FEC ssrc).
      if ((int) layer.getPrimarySSRC() == acceptedSSRC) {
        acceptedLayer = layer;
        break;
      }
    }

    // If this is not an RTP packet or if we can't find an accepted
    // layer, log and return as it makes no sense to continue in this
    // situation.
    if (acceptedLayer == null) {
      return;
    }

    // There are sequences of packets with increasing timestamps but without
    // the marker bit set. Supposedly, they are probes to detect whether the
    // bandwidth may increase. We think that they should cause neither the
    // start nor the stop of any SimulcastLayer.

    // XXX There's RawPacket#getPayloadLength() but the implementation
    // includes pkt.paddingSize at the time of this writing and we do not
    // know whether that's going to stay that way.
    int pktPayloadLength = pkt.getLength() - pkt.getHeaderLength();
    int pktPaddingSize = pkt.getPaddingSize();

    if (pktPayloadLength <= pktPaddingSize) {
      if (logger.isTraceEnabled()) {
        logger.trace(
            "pkt.payloadLength= "
                + pktPayloadLength
                + " <= pkt.paddingSize= "
                + pktPaddingSize
                + "("
                + pkt.getSequenceNumber()
                + ")");
      }
      return;
    }

    // NOTE(gp) we expect the base layer to be always on, so we never touch
    // it or starve it.

    // XXX Refer to the implementation of
    // SimulcastLayer#touch(boolean, RawPacket) for an explanation of why we
    // chose to use a return value.
    boolean frameStarted = acceptedLayer.touch(pkt);
    if (frameStarted) simulcastLayerFrameStarted(acceptedLayer, pkt, layers);
  }