/**
   * Creates a new <tt>RawPacket</tt> from a specific <tt>DatagramPacket</tt> in order to have this
   * instance receive its packet data through its {@link #read(byte[], int, int)} method. Returns an
   * array of <tt>RawPacket</tt> with the created packet as its first element (and <tt>null</tt> for
   * the other elements).
   *
   * <p>Allows extenders to intercept the packet data and possibly filter and/or modify it.
   *
   * @param datagramPacket the <tt>DatagramPacket</tt> containing the packet data
   * @return an array of <tt>RawPacket</tt> containing the <tt>RawPacket</tt> which contains the
   *     packet data of the specified <tt>DatagramPacket</tt> as its first element.
   */
  protected RawPacket[] createRawPacket(DatagramPacket datagramPacket) {
    RawPacket[] pkts = rawPacketArrayPool.poll();
    if (pkts == null) pkts = new RawPacket[1];

    RawPacket pkt = rawPacketPool.poll();
    if (pkt == null) pkt = new RawPacket();

    pkt.setBuffer(datagramPacket.getData());
    pkt.setFlags(0);
    pkt.setLength(datagramPacket.getLength());
    pkt.setOffset(datagramPacket.getOffset());

    pkts[0] = pkt;
    return pkts;
  }
 /**
  * Pools the specified <tt>RawPacket</tt> in order to avoid future allocations and to reduce the
  * effects of garbage collection.
  *
  * @param pkt the <tt>RawPacket</tt> to be offered to {@link #rawPacketPool}
  */
 private void poolRawPacket(RawPacket pkt) {
   pkt.setBuffer(null);
   pkt.setFlags(0);
   pkt.setLength(0);
   pkt.setOffset(0);
   rawPacketPool.offer(pkt);
 }
Example #3
0
  /**
   * Re-initializes the fields which store information about the currently held data. Empties
   * <tt>data</tt>.
   */
  private void reinit() {
    firstSeq = lastSeq = timestamp = -1;
    pictureId = -1;
    empty = true;
    haveEnd = haveStart = false;
    frameLength = 0;

    Iterator<Map.Entry<Long, Container>> it = data.entrySet().iterator();
    Map.Entry<Long, Container> e;
    while (it.hasNext()) {
      e = it.next();
      free.offer(e.getValue());
      it.remove();
    }
  }
Example #4
0
  /** {@inheritDoc} */
  @Override
  protected int doProcess(Buffer inBuffer, Buffer outBuffer) {
    byte[] inData = (byte[]) inBuffer.getData();
    int inOffset = inBuffer.getOffset();

    if (!VP8PayloadDescriptor.isValid(inData, inOffset)) {
      logger.warn("Invalid RTP/VP8 packet discarded.");
      outBuffer.setDiscard(true);
      return BUFFER_PROCESSED_FAILED; // XXX: FAILED or OK?
    }

    long inSeq = inBuffer.getSequenceNumber();
    long inRtpTimestamp = inBuffer.getRtpTimeStamp();
    int inPictureId = VP8PayloadDescriptor.getPictureId(inData, inOffset);
    boolean inMarker = (inBuffer.getFlags() & Buffer.FLAG_RTP_MARKER) != 0;
    boolean inIsStartOfFrame = VP8PayloadDescriptor.isStartOfFrame(inData, inOffset);
    int inLength = inBuffer.getLength();
    int inPdSize = VP8PayloadDescriptor.getSize(inData, inOffset);
    int inPayloadLength = inLength - inPdSize;

    if (empty && lastSentSeq != -1 && seqNumComparator.compare(inSeq, lastSentSeq) != 1) {
      if (logger.isInfoEnabled()) logger.info("Discarding old packet (while empty) " + inSeq);
      outBuffer.setDiscard(true);
      return BUFFER_PROCESSED_OK;
    }

    if (!empty) {
      // if the incoming packet has a different PictureID or timestamp
      // than those of the current frame, then it belongs to a different
      // frame.
      if ((inPictureId != -1 && pictureId != -1 && inPictureId != pictureId)
          | (timestamp != -1 && inRtpTimestamp != -1 && inRtpTimestamp != timestamp)) {
        if (seqNumComparator.compare(inSeq, firstSeq) != 1) // inSeq <= firstSeq
        {
          // the packet belongs to a previous frame. discard it
          if (logger.isInfoEnabled()) logger.info("Discarding old packet " + inSeq);
          outBuffer.setDiscard(true);
          return BUFFER_PROCESSED_OK;
        } else // inSeq > firstSeq (and also presumably isSeq > lastSeq)
        {
          // the packet belongs to a subsequent frame (to the one
          // currently being held). Drop the current frame.

          if (logger.isInfoEnabled())
            logger.info(
                "Discarding saved packets on arrival of"
                    + " a packet for a subsequent frame: "
                    + inSeq);

          // TODO: this would be the place to complain about the
          // not-well-received PictureID by sending a RTCP SLI or NACK.
          reinit();
        }
      }
    }

    // a whole frame in a single packet. avoid the extra copy to
    // this.data and output it immediately.
    if (empty && inMarker && inIsStartOfFrame) {
      byte[] outData = validateByteArraySize(outBuffer, inPayloadLength, false);
      System.arraycopy(inData, inOffset + inPdSize, outData, 0, inPayloadLength);
      outBuffer.setOffset(0);
      outBuffer.setLength(inPayloadLength);
      outBuffer.setRtpTimeStamp(inBuffer.getRtpTimeStamp());

      if (TRACE) logger.trace("Out PictureID=" + inPictureId);

      lastSentSeq = inSeq;

      return BUFFER_PROCESSED_OK;
    }

    // add to this.data
    Container container = free.poll();
    if (container == null) container = new Container();
    if (container.buf == null || container.buf.length < inPayloadLength)
      container.buf = new byte[inPayloadLength];

    if (data.get(inSeq) != null) {
      if (logger.isInfoEnabled())
        logger.info("(Probable) duplicate packet detected, discarding " + inSeq);
      outBuffer.setDiscard(true);
      return BUFFER_PROCESSED_OK;
    }

    System.arraycopy(inData, inOffset + inPdSize, container.buf, 0, inPayloadLength);
    container.len = inPayloadLength;
    data.put(inSeq, container);

    // update fields
    frameLength += inPayloadLength;
    if (firstSeq == -1 || (seqNumComparator.compare(firstSeq, inSeq) == 1)) firstSeq = inSeq;
    if (lastSeq == -1 || (seqNumComparator.compare(inSeq, lastSeq) == 1)) lastSeq = inSeq;

    if (empty) {
      // the first received packet for the current frame was just added
      empty = false;
      timestamp = inRtpTimestamp;
      pictureId = inPictureId;
    }

    if (inMarker) haveEnd = true;
    if (inIsStartOfFrame) haveStart = true;

    // check if we have a full frame
    if (frameComplete()) {
      byte[] outData = validateByteArraySize(outBuffer, frameLength, false);
      int ptr = 0;
      Container b;
      for (Map.Entry<Long, Container> entry : data.entrySet()) {
        b = entry.getValue();
        System.arraycopy(b.buf, 0, outData, ptr, b.len);
        ptr += b.len;
      }

      outBuffer.setOffset(0);
      outBuffer.setLength(frameLength);
      outBuffer.setRtpTimeStamp(inBuffer.getRtpTimeStamp());

      if (TRACE) logger.trace("Out PictureID=" + inPictureId);
      lastSentSeq = lastSeq;

      // prepare for the next frame
      reinit();

      return BUFFER_PROCESSED_OK;
    } else {
      // frame not complete yet
      outBuffer.setDiscard(true);
      return OUTPUT_BUFFER_NOT_FILLED;
    }
  }
  /**
   * Listens for incoming datagrams, stores them for reading by the <tt>read</tt> method and
   * notifies the local <tt>transferHandler</tt> that there's data to be read.
   */
  public void run() {
    DatagramPacket p = new DatagramPacket(buffer, 0, PACKET_RECEIVE_BUFFER_LENGTH);

    while (!closed) {
      try {
        // http://code.google.com/p/android/issues/detail?id=24765
        if (OSUtils.IS_ANDROID) p.setLength(PACKET_RECEIVE_BUFFER_LENGTH);

        receivePacket(p);
      } catch (IOException e) {
        ioError = true;
        break;
      }

      /*
       * Do the DatagramPacketFilters accept the received DatagramPacket?
       */
      DatagramPacketFilter[] datagramPacketFilters = getDatagramPacketFilters();
      boolean accept;

      if (!enabled) accept = false;
      else if (datagramPacketFilters == null) accept = true;
      else {
        accept = true;
        for (int i = 0; i < datagramPacketFilters.length; i++) {
          try {
            if (!datagramPacketFilters[i].accept(p)) {
              accept = false;
              break;
            }
          } catch (Throwable t) {
            if (t instanceof ThreadDeath) throw (ThreadDeath) t;
          }
        }
      }

      if (accept) {
        RawPacket pkts[] = createRawPacket(p);

        for (int i = 0; i < pkts.length; i++) {
          RawPacket pkt = pkts[i];

          pkts[i] = null;

          if (pkt != null) {
            if (pkt.isInvalid()) {
              /*
               * Return pkt to the pool because it is invalid and,
               * consequently, will not be made available to
               * reading.
               */
              poolRawPacket(pkt);
            } else {
              RawPacket oldPkt;

              synchronized (pktSyncRoot) {
                oldPkt = this.pkt;
                this.pkt = pkt;
              }
              if (oldPkt != null) {
                /*
                 * Return oldPkt to the pool because it was made
                 * available to reading and it was not read.
                 */
                poolRawPacket(oldPkt);
              }

              if (videoRecorder != null) videoRecorder.recordData(pkt);

              if ((transferHandler != null) && !closed) {
                try {
                  transferHandler.transferData(this);
                } catch (Throwable t) {
                  /*
                   * XXX We cannot allow transferHandler to
                   * kill us.
                   */
                  if (t instanceof ThreadDeath) {
                    throw (ThreadDeath) t;
                  } else {
                    logger.warn("An RTP packet may have not been" + " fully handled.", t);
                  }
                }
              }
            }
          }
        }
        rawPacketArrayPool.offer(pkts);
      }
    }
  }