/** * 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); }
/** * 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(); }
/** * 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); } } } }
/** * 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; }
/** * 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('>'); } }
/** * 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); } } }
/** * 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); }