Smack/smack-jingle-old/src/main/java/org/jivesoftware/smackx/jingleold/ContentNegotiator.java

358 lines
12 KiB
Java

/**
*
* Copyright the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.jingleold;
import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.SmackException.NoResponseException;
import org.jivesoftware.smack.SmackException.NotConnectedException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smackx.jingleold.listeners.JingleListener;
import org.jivesoftware.smackx.jingleold.listeners.JingleSessionListener;
import org.jivesoftware.smackx.jingleold.media.JingleMediaManager;
import org.jivesoftware.smackx.jingleold.media.JingleMediaSession;
import org.jivesoftware.smackx.jingleold.media.MediaNegotiator;
import org.jivesoftware.smackx.jingleold.media.PayloadType;
import org.jivesoftware.smackx.jingleold.nat.JingleTransportManager;
import org.jivesoftware.smackx.jingleold.nat.TransportCandidate;
import org.jivesoftware.smackx.jingleold.nat.TransportNegotiator;
import org.jivesoftware.smackx.jingleold.packet.Jingle;
import org.jivesoftware.smackx.jingleold.packet.JingleContent;
/**
* Content negotiator.
*
* @author Jeff Williams
*/
public class ContentNegotiator extends JingleNegotiator {
public static final String INITIATOR = "initiator";
public static final String RESPONDER = "responder";
private final List<TransportNegotiator> transportNegotiators;
private MediaNegotiator mediaNeg; // The description...
private TransportNegotiator transNeg; // and transport negotiators
private JingleTransportManager jingleTransportManager;
private final String creator;
private final String name;
private JingleMediaSession jingleMediaSession = null;
public ContentNegotiator(JingleSession session, String inCreator, String inName) {
super(session);
creator = inCreator;
name = inName;
transportNegotiators = new ArrayList<>();
}
@Override
public List<IQ> dispatchIncomingPacket(IQ iq, String id) throws XMPPException, SmackException, InterruptedException {
List<IQ> responses = new ArrayList<>();
// First only process IQ packets that contain <content> stanzas that
// match this media manager.
if (iq != null) {
if (iq.getType().equals(IQ.Type.error)) {
// Process errors
// TODO getState().eventError(iq);
} else if (iq.getType().equals(IQ.Type.result)) {
// Process ACKs
if (isExpectedId(iq.getStanzaId())) {
removeExpectedId(iq.getStanzaId());
}
} else if (iq instanceof Jingle) {
Jingle jingle = (Jingle) iq;
// There are 1 or more <content> sections in a Jingle packet.
// Find out which <content> section belongs to this content negotiator, and
// then dispatch the Jingle packet to the media and transport negotiators.
for (JingleContent jingleContent : jingle.getContentsList()) {
if (jingleContent.getName().equals(name)) {
if (mediaNeg != null) {
responses.addAll(mediaNeg.dispatchIncomingPacket(iq, id));
}
if (transNeg != null) {
responses.addAll(transNeg.dispatchIncomingPacket(iq, id));
}
}
}
}
}
return responses;
}
public String getCreator() {
return creator;
}
public String getName() {
return name;
}
/**
* Get the JingleMediaSession of this Jingle Session.
*
* @return the JingleMediaSession
*/
public JingleMediaSession getJingleMediaSession() {
return jingleMediaSession;
}
public void addTransportNegotiator(TransportNegotiator transportNegotiator) {
transportNegotiators.add(transportNegotiator);
}
/**
* Set jingle transport manager.
* @param jingleTransportManager TODO javadoc me please
*/
public void setJingleTransportManager(JingleTransportManager jingleTransportManager) {
this.jingleTransportManager = jingleTransportManager;
}
/**
* Get jingle transport manager.
* @return the JingleTransportManager
*/
public JingleTransportManager getTransportManager() {
return jingleTransportManager;
}
/**
* Called from above when starting a new session.
*/
@Override
protected void doStart() {
// JingleContent result = new JingleContent(creator, name);
// result.setDescription(mediaNeg.start());
// result.addJingleTransport(transNeg.start());
//
// return result;
mediaNeg.start();
transNeg.start();
}
/**
* Prepare to close the media manager.
*/
@Override
public void close() {
destroyMediaNegotiator();
destroyTransportNegotiator();
}
/**
* Obtain the description negotiator for this session.
*
* @return the description negotiator
*/
public MediaNegotiator getMediaNegotiator() {
return mediaNeg;
}
/**
* Set the jmf negotiator.
*
* @param mediaNeg TODO javadoc me please
* the description negotiator to set
*/
protected void setMediaNegotiator(MediaNegotiator mediaNeg) {
destroyMediaNegotiator();
this.mediaNeg = mediaNeg;
}
/**
* Destroy the jmf negotiator.
*/
protected void destroyMediaNegotiator() {
if (mediaNeg != null) {
mediaNeg.close();
mediaNeg = null;
}
}
/**
* Obtain the transport negotiator for this session.
*
* @return the transport negotiator instance
*/
public TransportNegotiator getTransportNegotiator() {
return transNeg;
}
/**
* Set TransportNegotiator
*
* @param transNeg TODO javadoc me please
* the transNeg to set
*/
protected void setTransportNegotiator(TransportNegotiator transNeg) {
destroyTransportNegotiator();
this.transNeg = transNeg;
}
/**
* Destroy the transport negotiator.
*/
protected void destroyTransportNegotiator() {
if (transNeg != null) {
transNeg.close();
transNeg = null;
}
}
/**
* Return true if the transport and content negotiators have finished.
*
* @return <code>true</code> if fully established.
*/
public boolean isFullyEstablished() {
boolean result = true;
MediaNegotiator mediaNeg = getMediaNegotiator();
if ((mediaNeg == null) || !mediaNeg.isFullyEstablished()) {
result = false;
}
TransportNegotiator transNeg = getTransportNegotiator();
if ((transNeg == null) || !transNeg.isFullyEstablished()) {
result = false;
}
return result;
}
public JingleContent getJingleContent() {
JingleContent result = new JingleContent(creator, name);
// PayloadType.Audio bestCommonAudioPt = getMediaNegotiator().getBestCommonAudioPt();
// TransportCandidate bestRemoteCandidate = getTransportNegotiator().getBestRemoteCandidate();
//
// // Ok, send a packet saying that we accept this session
// // with the audio payload type and the transport
// // candidate
// result.setDescription(new JingleDescription.Audio(new PayloadType(bestCommonAudioPt)));
// result.addJingleTransport(this.getTransportNegotiator().getJingleTransport(bestRemoteCandidate));
if (mediaNeg != null) {
result.setDescription(mediaNeg.getJingleDescription());
}
if (transNeg != null) {
result.addJingleTransport(transNeg.getJingleTransport());
}
return result;
}
public void triggerContentEstablished() throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException {
PayloadType bestCommonAudioPt = getMediaNegotiator().getBestCommonAudioPt();
TransportCandidate bestRemoteCandidate = getTransportNegotiator().getBestRemoteCandidate();
TransportCandidate acceptedLocalCandidate = getTransportNegotiator().getAcceptedLocalCandidate();
// Trigger the session established flag
triggerContentEstablished(bestCommonAudioPt, bestRemoteCandidate, acceptedLocalCandidate);
}
/**
* Trigger a session established event.
* @throws NotConnectedException if the XMPP connection is not connected.
* @throws InterruptedException if the calling thread was interrupted.
* @throws XMPPErrorException if there was an XMPP error returned.
* @throws NoResponseException if there was no response from the remote entity.
*/
private void triggerContentEstablished(PayloadType pt, TransportCandidate rc, TransportCandidate lc) throws NotConnectedException, InterruptedException, NoResponseException, XMPPErrorException {
// Let the session know that we've established a content/media segment.
JingleSession session = getSession();
if (session != null) {
List<JingleListener> listeners = session.getListenersList();
for (JingleListener li : listeners) {
if (li instanceof JingleSessionListener) {
JingleSessionListener sli = (JingleSessionListener) li;
sli.sessionEstablished(pt, rc, lc, session);
}
}
}
// Create a media session for each media manager in the session.
if (mediaNeg.getMediaManager() != null) {
rc.removeCandidateEcho();
lc.removeCandidateEcho();
jingleMediaSession = getMediaNegotiator().getMediaManager().createMediaSession(pt, rc, lc, session);
jingleMediaSession.addMediaReceivedListener(session);
if (jingleMediaSession != null) {
jingleMediaSession.startTransmit();
jingleMediaSession.startReceive();
for (TransportCandidate candidate : getTransportNegotiator().getOfferedCandidates())
candidate.removeCandidateEcho();
}
JingleMediaManager mediaManager = getMediaNegotiator().getMediaManager();
getSession().addJingleMediaSession(mediaManager.getName(), jingleMediaSession);
}
}
/**
* Stop a Jingle media session.
*/
public void stopJingleMediaSession() {
if (jingleMediaSession != null) {
jingleMediaSession.stopTransmit();
jingleMediaSession.stopReceive();
}
}
/**
* The negotiator state for the ContentNegotiators is a special case.
* It is a roll-up of the sub-negotiator states.
*/
@Override
public JingleNegotiatorState getNegotiatorState() {
JingleNegotiatorState result = JingleNegotiatorState.PENDING;
if ((mediaNeg != null) && (transNeg != null)) {
if ((mediaNeg.getNegotiatorState() == JingleNegotiatorState.SUCCEEDED)
|| (transNeg.getNegotiatorState() == JingleNegotiatorState.SUCCEEDED))
result = JingleNegotiatorState.SUCCEEDED;
if ((mediaNeg.getNegotiatorState() == JingleNegotiatorState.FAILED)
|| (transNeg.getNegotiatorState() == JingleNegotiatorState.FAILED))
result = JingleNegotiatorState.FAILED;
}
// Store the state (to make it easier to know when debugging.)
setNegotiatorState(result);
return result;
}
}