/**
   * Notifies this instance that {@link #speechActivity} has identified a speaker switch event in
   * this multipoint conference and there is now a new dominant speaker.
   */
  private void dominantSpeakerChanged() {
    Endpoint dominantSpeaker = speechActivity.getDominantEndpoint();

    if (logger.isTraceEnabled()) {
      logger.trace(
          "The dominant speaker in conference "
              + getID()
              + " is now the endpoint "
              + ((dominantSpeaker == null) ? "(null)" : dominantSpeaker.getID())
              + ".");
    }

    if (dominantSpeaker != null) {
      broadcastMessageOnDataChannels(createDominantSpeakerEndpointChangeEvent(dominantSpeaker));

      if (isRecording() && (recorderEventHandler != null))
        recorderEventHandler.dominantSpeakerChanged(dominantSpeaker);
    }
  }
Example #2
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;
    }
  }
  /**
   * 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);
  }