/**
   * Notifies this <tt>Conference</tt> that the ordered list of <tt>Endpoint</tt>s of {@link
   * #speechActivity} i.e. the dominant speaker history has changed.
   *
   * <p>This instance notifies the video <tt>Channel</tt>s about the change so that they may update
   * their last-n lists and report to this instance which <tt>Endpoint</tt>s are to be asked for
   * video keyframes.
   */
  private void speechActivityEndpointsChanged() {
    List<Endpoint> endpoints = null;

    for (Content content : getContents()) {
      if (MediaType.VIDEO.equals(content.getMediaType())) {
        Set<Endpoint> endpointsToAskForKeyframes = null;

        endpoints = speechActivity.getEndpoints();
        for (Channel channel : content.getChannels()) {
          if (!(channel instanceof RtpChannel)) continue;

          RtpChannel rtpChannel = (RtpChannel) channel;
          List<Endpoint> channelEndpointsToAskForKeyframes =
              rtpChannel.speechActivityEndpointsChanged(endpoints);

          if ((channelEndpointsToAskForKeyframes != null)
              && !channelEndpointsToAskForKeyframes.isEmpty()) {
            if (endpointsToAskForKeyframes == null) {
              endpointsToAskForKeyframes = new HashSet<>();
            }
            endpointsToAskForKeyframes.addAll(channelEndpointsToAskForKeyframes);
          }
        }

        if ((endpointsToAskForKeyframes != null) && !endpointsToAskForKeyframes.isEmpty()) {
          content.askForKeyframes(endpointsToAskForKeyframes);
        }
      }
    }
  }
  /**
   * Gets the <tt>MediaFormat</tt>s among the specified <tt>mediaFormats</tt> which have the
   * specified <tt>encoding</tt> and, optionally, <tt>clockRate</tt>.
   *
   * @param mediaFormats the <tt>MediaFormat</tt>s from which to filter out only the ones which have
   *     the specified <tt>encoding</tt> and, optionally, <tt>clockRate</tt>
   * @param encoding the well-known encoding (name) of the <tt>MediaFormat</tt>s to be retrieved
   * @param clockRate the clock rate of the <tt>MediaFormat</tt>s to be retrieved; {@link
   *     #CLOCK_RATE_NOT_SPECIFIED} if any clock rate is acceptable
   * @return a <tt>List</tt> of the <tt>MediaFormat</tt>s among <tt>mediaFormats</tt> which have the
   *     specified <tt>encoding</tt> and, optionally, <tt>clockRate</tt>
   */
  private List<MediaFormat> getMatchingMediaFormats(
      MediaFormat[] mediaFormats, String encoding, double clockRate) {
    /*
     * XXX Use String#equalsIgnoreCase(String) because some clients transmit
     * some of the codecs starting with capital letters.
     */

    /*
     * As per RFC 3551.4.5.2, because of a mistake in RFC 1890 and for
     * backward compatibility, G.722 should always be announced as 8000 even
     * though it is wideband. So, if someone is looking for G722/16000,
     * then: Forgive them, for they know not what they do!
     */
    if ("G722".equalsIgnoreCase(encoding) && (16000 == clockRate)) {
      clockRate = 8000;
      if (logger.isInfoEnabled()) logger.info("Suppressing erroneous 16000 announcement for G.722");
    }

    List<MediaFormat> supportedMediaFormats = new ArrayList<MediaFormat>();

    for (MediaFormat mediaFormat : mediaFormats) {
      if (mediaFormat.getEncoding().equalsIgnoreCase(encoding)
          && ((CLOCK_RATE_NOT_SPECIFIED == clockRate)
              || (mediaFormat.getClockRate() == clockRate))) {
        supportedMediaFormats.add(mediaFormat);
      }
    }
    return supportedMediaFormats;
  }
    /**
     * Adds a <tt>payload-type</tt> element defined by XEP-0167: Jingle RTP Sessions to this
     * <tt>channel</tt>.
     *
     * @param payloadType the <tt>payload-type</tt> element to be added to this <tt>channel</tt>
     * @return <tt>true</tt> if the list of <tt>payload-type</tt> elements associated with this
     *     <tt>channel</tt> has been modified as part of the method call; otherwise, <tt>false</tt>
     * @throws NullPointerException if the specified <tt>payloadType</tt> is <tt>null</tt>
     */
    public boolean addPayloadType(PayloadTypePacketExtension payloadType) {
      if (payloadType == null) throw new NullPointerException("payloadType");

      // Make sure that the COLIBRI namespace is used.
      payloadType.setNamespace(null);
      for (ParameterPacketExtension p : payloadType.getParameters()) p.setNamespace(null);

      return payloadTypes.contains(payloadType) ? false : payloadTypes.add(payloadType);
    }
예제 #4
0
  /**
   * Process candidates received.
   *
   * @param sessionInitIQ The {@link SessionIQ} that created the session we are handling here
   */
  public void processCandidates(SessionIQ sessionInitIQ) {
    Collection<PacketExtension> extensions = sessionInitIQ.getExtensions();
    List<GTalkCandidatePacketExtension> candidates = new ArrayList<GTalkCandidatePacketExtension>();

    for (PacketExtension ext : extensions) {
      if (ext.getElementName().equalsIgnoreCase(GTalkCandidatePacketExtension.ELEMENT_NAME)) {
        GTalkCandidatePacketExtension cand = (GTalkCandidatePacketExtension) ext;
        candidates.add(cand);
      }
    }

    try {
      getMediaHandler().processCandidates(candidates);
    } catch (OperationFailedException ofe) {
      logger.warn("Failed to process an incoming candidates", ofe);

      // send an error response
      String reasonText = "Error: " + ofe.getMessage();
      SessionIQ errResp =
          GTalkPacketFactory.createSessionTerminate(
              sessionInitIQ.getTo(),
              sessionInitIQ.getFrom(),
              sessionInitIQ.getID(),
              Reason.GENERAL_ERROR,
              reasonText);

      getMediaHandler().getTransportManager().close();
      setState(CallPeerState.FAILED, reasonText);
      getProtocolProvider().getConnection().sendPacket(errResp);
      return;
    }

    // HACK for FreeSwitch that send accept message before sending
    // candidates
    if (sessAcceptedWithNoCands != null) {
      if (isInitiator()) {
        try {
          answer();
        } catch (OperationFailedException e) {
          logger.info("Failed to answer call (FreeSwitch hack)");
        }
      } else {
        final SessionIQ sess = sessAcceptedWithNoCands;
        sessAcceptedWithNoCands = null;

        // run in another thread to not block smack receive thread and
        // possibly delay others candidates messages.
        new Thread() {
          @Override
          public void run() {
            processSessionAccept(sess);
          }
        }.start();
      }
      sessAcceptedWithNoCands = null;
    }
  }
    @Override
    protected boolean hasContent() {
      List<PayloadTypePacketExtension> payloadTypes = getPayloadTypes();
      boolean hasPayloadTypes = !payloadTypes.isEmpty();
      List<SourcePacketExtension> sources = getSources();
      boolean hasSources = !sources.isEmpty();
      int[] ssrcs = getSSRCs();
      boolean hasSSRCs = (ssrcs.length != 0);

      return hasPayloadTypes || hasSources || hasSSRCs;
    }
  /**
   * Expires a specific <tt>Content</tt> of this <tt>Conference</tt> (i.e. if the specified
   * <tt>content</tt> is not in the list of <tt>Content</tt>s of this <tt>Conference</tt>, does
   * nothing).
   *
   * @param content the <tt>Content</tt> to be expired by this <tt>Conference</tt>
   */
  public void expireContent(Content content) {
    boolean expireContent;

    synchronized (contents) {
      if (contents.contains(content)) {
        contents.remove(content);
        expireContent = true;
      } else expireContent = false;
    }
    if (expireContent) content.expire();
  }
예제 #7
0
 /**
  * Adds <tt>WebRtcDataStreamListener</tt> to the list of listeners.
  *
  * @param listener the <tt>WebRtcDataStreamListener</tt> to be added to the listeners list.
  */
 public void addChannelListener(WebRtcDataStreamListener listener) {
   if (listener == null) {
     throw new NullPointerException("listener");
   } else {
     synchronized (listeners) {
       if (!listeners.contains(listener)) {
         listeners.add(listener);
       }
     }
   }
 }
예제 #8
0
  /**
   * Gets the <tt>WebRtcDataStreamListener</tt>s added to this instance.
   *
   * @return the <tt>WebRtcDataStreamListener</tt>s added to this instance or <tt>null</tt> if there
   *     are no <tt>WebRtcDataStreamListener</tt>s added to this instance
   */
  private WebRtcDataStreamListener[] getChannelListeners() {
    WebRtcDataStreamListener[] ls;

    synchronized (listeners) {
      if (listeners.isEmpty()) {
        ls = null;
      } else {
        ls = listeners.toArray(new WebRtcDataStreamListener[listeners.size()]);
      }
    }
    return ls;
  }
  /**
   * Gets the <tt>MediaFormat</tt>s supported by this <tt>MediaFormatFactory</tt> and the
   * <tt>MediaService</tt> associated with it and having the specified <tt>encoding</tt> and,
   * optionally, <tt>clockRate</tt>.
   *
   * @param encoding the well-known encoding (name) of the <tt>MediaFormat</tt>s to be retrieved
   * @param clockRate the clock rate of the <tt>MediaFormat</tt>s to be retrieved; {@link
   *     #CLOCK_RATE_NOT_SPECIFIED} if any clock rate is acceptable
   * @return a <tt>List</tt> of the <tt>MediaFormat</tt>s supported by the <tt>MediaService</tt>
   *     associated with this <tt>MediaFormatFactory</tt> and having the specified encoding and,
   *     optionally, clock rate
   */
  private List<MediaFormat> getSupportedMediaFormats(String encoding, double clockRate) {
    EncodingConfiguration encodingConfiguration =
        NeomediaServiceUtils.getMediaServiceImpl().getCurrentEncodingConfiguration();
    List<MediaFormat> supportedMediaFormats =
        getMatchingMediaFormats(
            encodingConfiguration.getAllEncodings(MediaType.AUDIO), encoding, clockRate);

    if (supportedMediaFormats.isEmpty())
      supportedMediaFormats =
          getMatchingMediaFormats(
              encodingConfiguration.getAllEncodings(MediaType.VIDEO), encoding, clockRate);
    return supportedMediaFormats;
  }
예제 #10
0
  /**
   * Determines whether a specific format is supported by the <tt>Recorder</tt> represented by this
   * <tt>RecordButton</tt>.
   *
   * @param format the format which is to be checked whether it is supported by the
   *     <tt>Recorder</tt> represented by this <tt>RecordButton</tt>
   * @return <tt>true</tt> if the specified <tt>format</tt> is supported by the <tt>Recorder</tt>
   *     represented by this <tt>RecordButton</tt>; otherwise, <tt>false</tt>
   */
  private boolean isSupportedFormat(String format) {
    Recorder recorder;

    try {
      recorder = getRecorder();
    } catch (OperationFailedException ofex) {
      logger.error("Failed to get Recorder", ofex);
      return false;
    }

    List<String> supportedFormats = recorder.getSupportedFormats();

    return (supportedFormats != null) && supportedFormats.contains(format);
  }
    /**
     * Appends the XML <tt>String</tt> representation of this <tt>Content</tt> to a specific
     * <tt>StringBuilder</tt>.
     *
     * @param xml the <tt>StringBuilder</tt> to which the XML <tt>String</tt> representation of this
     *     <tt>Content</tt> is to be appended
     */
    public void toXML(StringBuilder xml) {
      xml.append('<').append(ELEMENT_NAME);
      xml.append(' ').append(NAME_ATTR_NAME).append("='").append(getName()).append('\'');

      List<Channel> channels = getChannels();
      List<SctpConnection> connections = getSctpConnections();

      if (channels.size() == 0 && connections.size() == 0) {
        xml.append(" />");
      } else {
        xml.append('>');
        for (Channel channel : channels) channel.toXML(xml);
        for (SctpConnection conn : connections) conn.toXML(xml);
        xml.append("</").append(ELEMENT_NAME).append('>');
      }
    }
예제 #12
0
 /**
  * Removes <tt>WebRtcDataStreamListener</tt> from the list of listeners.
  *
  * @param listener the <tt>WebRtcDataStreamListener</tt> to be removed from the listeners list.
  */
 public void removeChannelListener(WebRtcDataStreamListener listener) {
   if (listener != null) {
     synchronized (listeners) {
       listeners.remove(listener);
     }
   }
 }
예제 #13
0
 /**
  * Gets and formats the names of the peers in the call.
  *
  * @param maxLength maximum length of the filename
  * @return the name of the peer in the call formated
  */
 private String getCallPeerName(int maxLength) {
   List<CallPeer> callPeers = call.getConference().getCallPeers();
   CallPeer callPeer = null;
   String peerName = "";
   if (!callPeers.isEmpty()) {
     callPeer = callPeers.get(0);
     if (callPeer != null) {
       peerName = callPeer.getDisplayName();
       peerName = peerName.replaceAll("[^\\da-zA-Z\\_\\-@\\.]", "");
       if (peerName.length() > maxLength) {
         peerName = peerName.substring(0, maxLength);
       }
     }
   }
   return peerName;
 }
  /**
   * Returns an XML <tt>String</tt> representation of this <tt>IQ</tt>.
   *
   * @return an XML <tt>String</tt> representation of this <tt>IQ</tt>
   */
  @Override
  public String getChildElementXML() {
    StringBuilder xml = new StringBuilder();

    xml.append('<').append(ELEMENT_NAME);
    xml.append(" xmlns='").append(NAMESPACE).append('\'');

    String id = getID();

    if (id != null) xml.append(' ').append(ID_ATTR_NAME).append("='").append(id).append('\'');

    List<Content> contents = getContents();

    if (contents.size() == 0) {
      xml.append(" />");
    } else {
      xml.append('>');
      for (Content content : contents) content.toXML(xml);
      xml.append("</").append(ELEMENT_NAME).append('>');
    }
    return xml.toString();
  }
  /**
   * Gets the <tt>Endpoint</tt>s participating in/contributing to this <tt>Conference</tt>.
   *
   * @return the <tt>Endpoint</tt>s participating in/contributing to this <tt>Conference</tt>
   */
  public List<Endpoint> getEndpoints() {
    List<Endpoint> endpoints;
    boolean changed = false;

    synchronized (this.endpoints) {
      endpoints = new ArrayList<>(this.endpoints.size());
      for (Iterator<WeakReference<Endpoint>> i = this.endpoints.iterator(); i.hasNext(); ) {
        Endpoint endpoint = i.next().get();

        if (endpoint == null) {
          i.remove();
          changed = true;
        } else {
          endpoints.add(endpoint);
        }
      }
    }

    if (changed) firePropertyChange(ENDPOINTS_PROPERTY_NAME, null, null);

    return endpoints;
  }
  /**
   * Gets a <tt>Content</tt> of this <tt>Conference</tt> which has a specific name. If a
   * <tt>Content</tt> of this <tt>Conference</tt> with the specified <tt>name</tt> does not exist at
   * the time the method is invoked, the method initializes a new <tt>Content</tt> instance with the
   * specified <tt>name</tt> and adds it to the list of <tt>Content</tt>s of this
   * <tt>Conference</tt>.
   *
   * @param name the name of the <tt>Content</tt> which is to be returned
   * @return a <tt>Content</tt> of this <tt>Conference</tt> which has the specified <tt>name</tt>
   */
  public Content getOrCreateContent(String name) {
    Content content;

    synchronized (contents) {
      for (Content aContent : contents) {
        if (aContent.getName().equals(name)) {
          aContent.touch(); // It seems the content is still active.
          return aContent;
        }
      }

      content = new Content(this, name);
      if (isRecording()) {
        content.setRecording(true, getRecordingPath());
      }
      contents.add(content);
    }

    if (logger.isInfoEnabled()) {
      /*
       * The method Videobridge.getChannelCount() should better be
       * executed outside synchronized blocks in order to reduce the risks
       * of causing deadlocks.
       */
      Videobridge videobridge = getVideobridge();

      logger.info(
          "Created content "
              + name
              + " of conference "
              + getID()
              + ". "
              + videobridge.getConferenceCountString());
    }

    return content;
  }
    /**
     * Adds a specific <tt>Channel</tt> to the list of <tt>Channel</tt>s included into this
     * <tt>Content</tt>.
     *
     * @param channel the <tt>Channel</tt> to be included into this <tt>Content</tt>
     * @return <tt>true</tt> if the list of <tt>Channel</tt>s included into this <tt>Content</tt>
     *     was modified as a result of the execution of the method; otherwise, <tt>false</tt>
     * @throws NullPointerException if the specified <tt>channel</tt> is <tt>null</tt>
     */
    public boolean addChannel(Channel channel) {
      if (channel == null) throw new NullPointerException("channel");

      return channels.contains(channel) ? false : channels.add(channel);
    }
    /**
     * Adds a specific <tt>SctpConnection</tt> to the list of <tt>SctpConnection</tt>s included into
     * this <tt>Content</tt>.
     *
     * @param conn the <tt>SctpConnection</tt> to be included into this <tt>Content</tt>
     * @return <tt>true</tt> if the list of <tt>SctpConnection</tt>s included into this
     *     <tt>Content</tt> was modified as a result of the execution of the method; otherwise,
     *     <tt>false</tt>
     * @throws NullPointerException if the specified <tt>conn</tt> is <tt>null</tt>
     */
    public boolean addSctpConnection(SctpConnection conn) {
      if (conn == null) throw new NullPointerException("Sctp connection");

      return !sctpConnections.contains(conn) && sctpConnections.add(conn);
    }
 /**
  * Removes a specific <tt>Channel</tt> from the list of <tt>Channel</tt>s included into this
  * <tt>Content</tt>.
  *
  * @param channel the <tt>Channel</tt> to be excluded from this <tt>Content</tt>
  * @return <tt>true</tt> if the list of <tt>Channel</tt>s included into this <tt>Content</tt>
  *     was modified as a result of the execution of the method; otherwise, <tt>false</tt>
  */
 public boolean removeChannel(Channel channel) {
   return channels.remove(channel);
 }
 /**
  * Removes a specific {@link Content} instance from the list of <tt>Content</tt> instances
  * included into this <tt>conference</tt> IQ.
  *
  * @param content the <tt>Content</tt> instance to be removed from the list of <tt>Content</tt>
  *     instances included into this <tt>conference</tt> IQ
  * @return <tt>true</tt> if the list of <tt>Content</tt> instances included into this
  *     <tt>conference</tt> IQ has been modified as a result of the method call; otherwise,
  *     <tt>false</tt>
  */
 public boolean removeContent(Content content) {
   return contents.remove(content);
 }
 /**
  * Gets the <tt>Content</tt>s of this <tt>Conference</tt>.
  *
  * @return the <tt>Content</tt>s of this <tt>Conference</tt>
  */
 public Content[] getContents() {
   synchronized (contents) {
     return contents.toArray(new Content[contents.size()]);
   }
 }
    /**
     * Adds a <tt>SourcePacketExtension</tt> to the list of sources of this channel.
     *
     * @param source the <tt>SourcePacketExtension</tt> to add to the list of sources of this
     *     channel
     * @return <tt>true</tt> if the list of sources of this channel changed as a result of the
     *     execution of the method; otherwise, <tt>false</tt>
     */
    public synchronized boolean addSource(SourcePacketExtension source) {
      if (source == null) throw new NullPointerException("source");

      return sources.contains(source) ? false : sources.add(source);
    }
 /**
  * Removes a <tt>payload-type</tt> element defined by XEP-0167: Jingle RTP Sessions from this
  * <tt>channel</tt>.
  *
  * @param payloadType the <tt>payload-type</tt> element to be removed from this <tt>channel</tt>
  * @return <tt>true</tt> if the list of <tt>payload-type</tt> elements associated with this
  *     <tt>channel</tt> has been modified as part of the method call; otherwise, <tt>false</tt>
  */
 public boolean removePayloadType(PayloadTypePacketExtension payloadType) {
   return payloadTypes.remove(payloadType);
 }
 /**
  * Removes a <tt>SourcePacketExtension</tt> from the list of sources of this channel.
  *
  * @param source the <tt>SourcePacketExtension</tt> to remove from the list of sources of this
  *     channel
  * @return <tt>true</tt> if the list of sources of this channel changed as a result of the
  *     execution of the method; otherwise, <tt>false</tt>
  */
 public synchronized boolean removeSource(SourcePacketExtension source) {
   return sources.remove(source);
 }
  /**
   * Adds a specific {@link Content} instance to the list of <tt>Content</tt> instances included
   * into this <tt>conference</tt> IQ.
   *
   * @param content the <tt>Content</tt> instance to be added to this list of <tt>Content</tt>
   *     instances included into this <tt>conference</tt> IQ
   * @return <tt>true</tt> if the list of <tt>Content</tt> instances included into this
   *     <tt>conference</tt> IQ has been modified as a result of the method call; otherwise,
   *     <tt>false</tt>
   * @throws NullPointerException if the specified <tt>content</tt> is <tt>null</tt>
   */
  public boolean addContent(Content content) {
    if (content == null) throw new NullPointerException("content");

    return contents.contains(content) ? false : contents.add(content);
  }