2007-03-03 04:48:59 +01:00
|
|
|
/**
|
|
|
|
* $RCSfile$
|
|
|
|
* $Revision: 7329 $
|
|
|
|
* $Date: 2007-02-28 20:59:28 -0300 (qua, 28 fev 2007) $
|
|
|
|
*
|
|
|
|
* Copyright 2003-2005 Jive Software.
|
|
|
|
*
|
|
|
|
* All rights reserved. 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.
|
|
|
|
*/
|
2007-01-04 18:25:30 +01:00
|
|
|
package org.jivesoftware.smackx.jingle.media;
|
|
|
|
|
|
|
|
import org.jivesoftware.smack.XMPPException;
|
|
|
|
import org.jivesoftware.smack.packet.IQ;
|
|
|
|
import org.jivesoftware.smackx.jingle.JingleNegotiator;
|
|
|
|
import org.jivesoftware.smackx.jingle.JingleSession;
|
|
|
|
import org.jivesoftware.smackx.jingle.listeners.JingleListener;
|
|
|
|
import org.jivesoftware.smackx.jingle.listeners.JingleMediaListener;
|
|
|
|
import org.jivesoftware.smackx.packet.Jingle;
|
|
|
|
import org.jivesoftware.smackx.packet.JingleContentDescription;
|
|
|
|
import org.jivesoftware.smackx.packet.JingleContentDescription.JinglePayloadType;
|
|
|
|
import org.jivesoftware.smackx.packet.JingleError;
|
|
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
|
import java.util.Iterator;
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Manager for jmf descriptor negotiation.
|
2007-03-03 04:48:59 +01:00
|
|
|
* <p/>
|
|
|
|
* <p/>
|
2007-01-04 18:25:30 +01:00
|
|
|
* This class is responsible for managing the descriptor negotiation process,
|
|
|
|
* handling all the xmpp packets interchange and the stage control.
|
2007-03-27 17:44:07 +02:00
|
|
|
* handling all the xmpp packets interchange and the stage control.
|
2007-03-03 04:48:59 +01:00
|
|
|
*
|
|
|
|
* @author Thiago Camargo
|
2007-01-04 18:25:30 +01:00
|
|
|
*/
|
|
|
|
public class MediaNegotiator extends JingleNegotiator {
|
|
|
|
|
|
|
|
private final JingleSession session; // The session this negotiation
|
|
|
|
|
|
|
|
// Local and remote payload types...
|
|
|
|
|
|
|
|
private final List<PayloadType> localAudioPts = new ArrayList<PayloadType>();
|
|
|
|
|
|
|
|
private final List<PayloadType> remoteAudioPts = new ArrayList<PayloadType>();
|
|
|
|
|
|
|
|
private PayloadType.Audio bestCommonAudioPt;
|
|
|
|
|
|
|
|
// states
|
|
|
|
|
|
|
|
private final Inviting inviting;
|
|
|
|
|
|
|
|
private final Accepting accepting;
|
|
|
|
|
|
|
|
private final Pending pending;
|
|
|
|
|
|
|
|
private final Active active;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Default constructor. The constructor establishes some basic parameters,
|
|
|
|
* but it does not start the negotiation. For starting the negotiation, call
|
|
|
|
* startNegotiation.
|
|
|
|
*
|
|
|
|
* @param js The jingle session.
|
|
|
|
*/
|
|
|
|
public MediaNegotiator(JingleSession js, List<PayloadType> pts) {
|
|
|
|
super(js.getConnection());
|
|
|
|
|
|
|
|
session = js;
|
|
|
|
|
|
|
|
bestCommonAudioPt = null;
|
|
|
|
|
|
|
|
if (pts != null) {
|
|
|
|
if (pts.size() > 0) {
|
|
|
|
localAudioPts.addAll(pts);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create the states...
|
|
|
|
inviting = new Inviting(this);
|
|
|
|
accepting = new Accepting(this);
|
|
|
|
pending = new Pending(this);
|
|
|
|
active = new Active(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Dispatch an incomming packet. The medthod is responsible for recognizing
|
|
|
|
* the packet type and, depending on the current state, deliverying the
|
|
|
|
* packet to the right event handler and wait for a response.
|
|
|
|
*
|
|
|
|
* @param iq the packet received
|
|
|
|
* @return the new Jingle packet to send.
|
|
|
|
* @throws XMPPException
|
|
|
|
*/
|
|
|
|
public IQ dispatchIncomingPacket(IQ iq, String id) throws XMPPException {
|
|
|
|
IQ jout = null;
|
|
|
|
|
|
|
|
if (invalidState()) {
|
|
|
|
if (iq == null) {
|
|
|
|
// With a null packet, we are just inviting the other end...
|
|
|
|
setState(inviting);
|
|
|
|
jout = getState().eventInvite();
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
if (iq instanceof Jingle) {
|
|
|
|
// If there is no specific jmf action associated, then we
|
|
|
|
// are being invited to a new session...
|
|
|
|
setState(accepting);
|
|
|
|
jout = getState().eventInitiate((Jingle) iq);
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
throw new IllegalStateException(
|
|
|
|
"Invitation IQ received is not a Jingle packet in Media negotiator.");
|
|
|
|
}
|
|
|
|
}
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
if (iq == null) {
|
|
|
|
return null;
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
if (iq.getType().equals(IQ.Type.ERROR)) {
|
|
|
|
// Process errors
|
|
|
|
getState().eventError(iq);
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else if (iq.getType().equals(IQ.Type.RESULT)) {
|
2007-01-04 18:25:30 +01:00
|
|
|
// Process ACKs
|
|
|
|
if (isExpectedId(iq.getPacketID())) {
|
|
|
|
jout = getState().eventAck(iq);
|
|
|
|
removeExpectedId(iq.getPacketID());
|
|
|
|
}
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else if (iq instanceof Jingle) {
|
2007-01-04 18:25:30 +01:00
|
|
|
// Get the action from the Jingle packet
|
|
|
|
Jingle jin = (Jingle) iq;
|
|
|
|
Jingle.Action action = jin.getAction();
|
|
|
|
|
|
|
|
if (action != null) {
|
|
|
|
if (action.equals(Jingle.Action.CONTENTACCEPT)) {
|
|
|
|
jout = getState().eventAccept(jin);
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else if (action.equals(Jingle.Action.CONTENTDECLINE)) {
|
2007-01-04 18:25:30 +01:00
|
|
|
jout = getState().eventDecline(jin);
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else if (action.equals(Jingle.Action.DESCRIPTIONINFO)) {
|
2007-01-04 18:25:30 +01:00
|
|
|
jout = getState().eventInfo(jin);
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else if (action.equals(Jingle.Action.CONTENTMODIFY)) {
|
2007-01-04 18:25:30 +01:00
|
|
|
jout = getState().eventModify(jin);
|
|
|
|
}
|
|
|
|
// Any unknown action will be ignored: it is not a msg
|
|
|
|
// to us...
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Save the Id for any ACK
|
|
|
|
if (id != null) {
|
|
|
|
addExpectedId(id);
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
if (jout != null) {
|
|
|
|
addExpectedId(jout.getPacketID());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return jout;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return true if the content is negotiated.
|
|
|
|
*
|
|
|
|
* @return true if the content is negotiated.
|
|
|
|
*/
|
|
|
|
public boolean isEstablished() {
|
|
|
|
return getBestCommonAudioPt() != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return true if the content is fully negotiated.
|
|
|
|
*
|
|
|
|
* @return true if the content is fully negotiated.
|
|
|
|
*/
|
|
|
|
public boolean isFullyEstablished() {
|
|
|
|
return isEstablished() && getState() == active;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Payload types
|
|
|
|
|
|
|
|
private PayloadType.Audio calculateBestCommonAudioPt(List remoteAudioPts) {
|
2007-03-03 04:48:59 +01:00
|
|
|
final ArrayList<PayloadType> commonAudioPtsHere = new ArrayList<PayloadType>();
|
|
|
|
final ArrayList<PayloadType> commonAudioPtsThere = new ArrayList<PayloadType>();
|
2007-01-04 18:25:30 +01:00
|
|
|
PayloadType.Audio result = null;
|
|
|
|
|
|
|
|
if (!remoteAudioPts.isEmpty()) {
|
|
|
|
commonAudioPtsHere.addAll(localAudioPts);
|
|
|
|
commonAudioPtsHere.retainAll(remoteAudioPts);
|
|
|
|
|
|
|
|
commonAudioPtsThere.addAll(remoteAudioPts);
|
|
|
|
commonAudioPtsThere.retainAll(localAudioPts);
|
|
|
|
|
|
|
|
if (!commonAudioPtsHere.isEmpty() && !commonAudioPtsThere.isEmpty()) {
|
|
|
|
|
2007-03-17 21:13:05 +01:00
|
|
|
if (session.getInitiator().equals(session.getConnection().getUser())) {
|
|
|
|
PayloadType.Audio bestPtHere = null;
|
2007-03-03 04:48:59 +01:00
|
|
|
|
2007-03-17 21:13:05 +01:00
|
|
|
PayloadType payload = this.session.getMediaManager().getPreferredPayloadType();
|
|
|
|
|
|
|
|
if (payload != null && payload instanceof PayloadType.Audio)
|
|
|
|
if (commonAudioPtsHere.contains(payload))
|
|
|
|
bestPtHere = (PayloadType.Audio) payload;
|
|
|
|
|
|
|
|
if (bestPtHere == null)
|
|
|
|
for (PayloadType payloadType : commonAudioPtsHere)
|
|
|
|
if (payloadType instanceof PayloadType.Audio) {
|
|
|
|
bestPtHere = (PayloadType.Audio) payloadType;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = bestPtHere;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
PayloadType.Audio bestPtThere = null;
|
|
|
|
for (PayloadType payloadType : commonAudioPtsThere)
|
2007-03-03 04:48:59 +01:00
|
|
|
if (payloadType instanceof PayloadType.Audio) {
|
2007-03-17 21:13:05 +01:00
|
|
|
bestPtThere = (PayloadType.Audio) payloadType;
|
2007-03-03 04:48:59 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = bestPtThere;
|
2007-03-17 21:13:05 +01:00
|
|
|
}
|
2007-01-04 18:25:30 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private List obtainPayloads(Jingle jin) {
|
|
|
|
List result = new ArrayList();
|
|
|
|
Iterator iDescr = jin.getDescriptions();
|
|
|
|
|
|
|
|
// Add the list of payloads: iterate over the descriptions...
|
|
|
|
while (iDescr.hasNext()) {
|
|
|
|
JingleContentDescription.Audio descr = (JingleContentDescription.Audio) iDescr
|
|
|
|
.next();
|
|
|
|
|
|
|
|
if (descr != null) {
|
|
|
|
// ...and, then, over the payloads.
|
|
|
|
// Note: we use the last "description" in the packet...
|
|
|
|
result.clear();
|
|
|
|
result.addAll(descr.getAudioPayloadTypesList());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Adds a payload type to the list of remote payloads.
|
|
|
|
*
|
|
|
|
* @param pt the remote payload type
|
|
|
|
*/
|
|
|
|
public void addRemoteAudioPayloadType(PayloadType.Audio pt) {
|
|
|
|
if (pt != null) {
|
|
|
|
synchronized (remoteAudioPts) {
|
|
|
|
remoteAudioPts.add(pt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an offer for the list of audio payload types.
|
|
|
|
*
|
|
|
|
* @return a new Jingle packet with the list of audio Payload Types
|
|
|
|
*/
|
|
|
|
private Jingle getAudioPayloadTypesOffer() {
|
|
|
|
JingleContentDescription.Audio audioDescr = new JingleContentDescription.Audio();
|
|
|
|
|
|
|
|
// Add the list of payloads for audio and create a
|
|
|
|
// JingleContentDescription
|
|
|
|
// where we announce our payloads...
|
|
|
|
audioDescr.addAudioPayloadTypes(localAudioPts);
|
|
|
|
|
|
|
|
return new Jingle(audioDescr);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Predefined messages and Errors
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an IQ "accept" message.
|
|
|
|
*/
|
|
|
|
private Jingle createAcceptMessage() {
|
|
|
|
Jingle jout = null;
|
|
|
|
|
|
|
|
// If we hava a common best codec, send an accept right now...
|
|
|
|
jout = new Jingle(Jingle.Action.CONTENTACCEPT);
|
|
|
|
jout.addDescription(new JingleContentDescription.Audio(
|
|
|
|
new JinglePayloadType.Audio(bestCommonAudioPt)));
|
|
|
|
|
|
|
|
return jout;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Payloads
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Get the best common codec between both parts.
|
|
|
|
*
|
|
|
|
* @return The best common PayloadType codec.
|
|
|
|
*/
|
|
|
|
public PayloadType.Audio getBestCommonAudioPt() {
|
|
|
|
return bestCommonAudioPt;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Events
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger a session established event.
|
|
|
|
*
|
|
|
|
* @param bestPt payload type that has been agreed.
|
|
|
|
*/
|
|
|
|
protected void triggerMediaEstablished(PayloadType bestPt) {
|
|
|
|
ArrayList listeners = getListenersList();
|
|
|
|
Iterator iter = listeners.iterator();
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
JingleListener li = (JingleListener) iter.next();
|
|
|
|
if (li instanceof JingleMediaListener) {
|
|
|
|
JingleMediaListener mli = (JingleMediaListener) li;
|
|
|
|
mli.mediaEstablished(bestPt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Trigger a jmf closed event.
|
|
|
|
*
|
|
|
|
* @param currPt current payload type that is cancelled.
|
|
|
|
*/
|
|
|
|
protected void triggerMediaClosed(PayloadType currPt) {
|
|
|
|
ArrayList listeners = getListenersList();
|
|
|
|
Iterator iter = listeners.iterator();
|
|
|
|
while (iter.hasNext()) {
|
|
|
|
JingleListener li = (JingleListener) iter.next();
|
|
|
|
if (li instanceof JingleMediaListener) {
|
|
|
|
JingleMediaListener mli = (JingleMediaListener) li;
|
|
|
|
mli.mediaClosed(currPt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Terminate the jmf negotiator
|
|
|
|
*/
|
|
|
|
public void close() {
|
|
|
|
super.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
// States
|
|
|
|
|
|
|
|
/**
|
|
|
|
* First stage when we send a session request.
|
|
|
|
*/
|
|
|
|
public class Inviting extends JingleNegotiator.State {
|
2007-03-03 04:48:59 +01:00
|
|
|
|
2007-01-04 18:25:30 +01:00
|
|
|
public Inviting(MediaNegotiator neg) {
|
|
|
|
super(neg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Create an initial Jingle packet, with the list of payload types that
|
|
|
|
* we support. The list is in order of preference.
|
|
|
|
*/
|
|
|
|
public Jingle eventInvite() {
|
|
|
|
return getAudioPayloadTypesOffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We have received the ACK for our invitation.
|
|
|
|
*
|
|
|
|
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
|
|
|
|
*/
|
|
|
|
public Jingle eventAck(IQ iq) {
|
|
|
|
setState(pending);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We are accepting connections.
|
|
|
|
*/
|
|
|
|
public class Accepting extends JingleNegotiator.State {
|
|
|
|
|
|
|
|
public Accepting(MediaNegotiator neg) {
|
|
|
|
super(neg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We have received an invitation! Respond with a list of our payload
|
|
|
|
* types...
|
|
|
|
*/
|
|
|
|
public Jingle eventInitiate(Jingle jin) {
|
|
|
|
synchronized (remoteAudioPts) {
|
|
|
|
remoteAudioPts.addAll(obtainPayloads(jin));
|
|
|
|
}
|
|
|
|
|
|
|
|
return getAudioPayloadTypesOffer();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Process the ACK of our list of codecs (our offer).
|
|
|
|
*
|
|
|
|
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
|
|
|
|
*/
|
|
|
|
public Jingle eventAck(IQ iq) throws XMPPException {
|
|
|
|
Jingle response = null;
|
|
|
|
|
|
|
|
if (!remoteAudioPts.isEmpty()) {
|
|
|
|
// Calculate the best common codec
|
|
|
|
bestCommonAudioPt = calculateBestCommonAudioPt(remoteAudioPts);
|
|
|
|
|
|
|
|
// and send an accept if we havee an agreement...
|
|
|
|
if (bestCommonAudioPt != null) {
|
|
|
|
response = createAcceptMessage();
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
throw new JingleException(JingleError.NO_COMMON_PAYLOAD);
|
|
|
|
}
|
|
|
|
|
|
|
|
setState(pending);
|
|
|
|
}
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pending class: we are waiting for the other enpoint, that must say if it
|
|
|
|
* accepts or not...
|
|
|
|
*/
|
|
|
|
public class Pending extends JingleNegotiator.State {
|
2007-03-03 04:48:59 +01:00
|
|
|
|
2007-01-04 18:25:30 +01:00
|
|
|
public Pending(MediaNegotiator neg) {
|
|
|
|
super(neg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A content info has been received. This is done for publishing the
|
|
|
|
* list of payload types...
|
|
|
|
*
|
|
|
|
* @param jin The input packet
|
|
|
|
* @return a Jingle packet
|
|
|
|
* @throws JingleException
|
|
|
|
*/
|
|
|
|
public Jingle eventInfo(Jingle jin) throws JingleException {
|
|
|
|
PayloadType.Audio oldBestCommonAudioPt = bestCommonAudioPt;
|
2007-03-27 17:44:07 +02:00
|
|
|
List offeredPayloads;
|
2007-01-04 18:25:30 +01:00
|
|
|
Jingle response = null;
|
|
|
|
boolean ptChange = false;
|
|
|
|
|
|
|
|
offeredPayloads = obtainPayloads(jin);
|
|
|
|
if (!offeredPayloads.isEmpty()) {
|
|
|
|
|
|
|
|
synchronized (remoteAudioPts) {
|
|
|
|
remoteAudioPts.clear();
|
|
|
|
remoteAudioPts.addAll(offeredPayloads);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the best common codec
|
|
|
|
bestCommonAudioPt = calculateBestCommonAudioPt(remoteAudioPts);
|
|
|
|
if (bestCommonAudioPt != null) {
|
|
|
|
// and send an accept if we have an agreement...
|
|
|
|
ptChange = !bestCommonAudioPt.equals(oldBestCommonAudioPt);
|
|
|
|
if (oldBestCommonAudioPt == null || ptChange) {
|
|
|
|
response = createAcceptMessage();
|
|
|
|
}
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else {
|
2007-01-04 18:25:30 +01:00
|
|
|
throw new JingleException(JingleError.NO_COMMON_PAYLOAD);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the Jingle and get the payload accepted
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A jmf description has been accepted. In this case, we must save the
|
|
|
|
* accepted payload type and notify any listener...
|
|
|
|
*
|
|
|
|
* @param jin The input packet
|
|
|
|
* @return a Jingle packet
|
|
|
|
* @throws JingleException
|
|
|
|
*/
|
|
|
|
public Jingle eventAccept(Jingle jin) throws JingleException {
|
|
|
|
PayloadType.Audio agreedCommonAudioPt;
|
|
|
|
List offeredPayloads = new ArrayList();
|
|
|
|
Jingle response = null;
|
|
|
|
|
|
|
|
if (bestCommonAudioPt == null) {
|
|
|
|
// Update the best common audio PT
|
|
|
|
bestCommonAudioPt = calculateBestCommonAudioPt(remoteAudioPts);
|
|
|
|
response = createAcceptMessage();
|
|
|
|
}
|
|
|
|
|
|
|
|
offeredPayloads = obtainPayloads(jin);
|
|
|
|
if (!offeredPayloads.isEmpty()) {
|
|
|
|
if (offeredPayloads.size() == 1) {
|
|
|
|
agreedCommonAudioPt = (PayloadType.Audio) offeredPayloads.get(0);
|
|
|
|
if (bestCommonAudioPt != null) {
|
|
|
|
// If the accepted PT matches the best payload
|
|
|
|
// everything is fine
|
|
|
|
if (!agreedCommonAudioPt.equals(bestCommonAudioPt)) {
|
|
|
|
throw new JingleException(JingleError.NEGOTIATION_ERROR);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-03 04:48:59 +01:00
|
|
|
}
|
|
|
|
else if (offeredPayloads.size() > 1) {
|
2007-01-04 18:25:30 +01:00
|
|
|
throw new JingleException(JingleError.MALFORMED_STANZA);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return response;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The other part has declined the our codec...
|
|
|
|
*
|
|
|
|
* @throws JingleException
|
|
|
|
*/
|
|
|
|
public Jingle eventDecline(Jingle inJingle) throws JingleException {
|
|
|
|
triggerMediaClosed(getBestCommonAudioPt());
|
|
|
|
throw new JingleException();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* (non-Javadoc)
|
|
|
|
*
|
|
|
|
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventError(org.jivesoftware.smack.packet.IQ)
|
|
|
|
*/
|
|
|
|
public void eventError(IQ iq) throws XMPPException {
|
|
|
|
triggerMediaClosed(getBestCommonAudioPt());
|
|
|
|
super.eventError(iq);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* ACK received.
|
|
|
|
*
|
|
|
|
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventAck(org.jivesoftware.smack.packet.IQ)
|
|
|
|
*/
|
|
|
|
public Jingle eventAck(IQ iq) {
|
2007-03-27 17:44:07 +02:00
|
|
|
|
2007-01-04 18:25:30 +01:00
|
|
|
if (isEstablished()) {
|
|
|
|
setState(active);
|
2007-03-27 17:44:07 +02:00
|
|
|
return null;
|
2007-01-04 18:25:30 +01:00
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* "Active" state: we have an agreement about the codec...
|
|
|
|
*/
|
|
|
|
public class Active extends JingleNegotiator.State {
|
|
|
|
|
|
|
|
public Active(MediaNegotiator neg) {
|
|
|
|
super(neg);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We have an agreement.
|
|
|
|
*
|
|
|
|
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventEnter()
|
|
|
|
*/
|
|
|
|
public void eventEnter() {
|
|
|
|
triggerMediaEstablished(getBestCommonAudioPt());
|
2007-03-27 17:44:07 +02:00
|
|
|
System.err.println("BS:"+getBestCommonAudioPt().getName());
|
2007-01-04 18:25:30 +01:00
|
|
|
super.eventEnter();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* We are breaking the contract...
|
|
|
|
*
|
|
|
|
* @see org.jivesoftware.smackx.jingle.JingleNegotiator.State#eventExit()
|
|
|
|
*/
|
|
|
|
public void eventExit() {
|
|
|
|
triggerMediaClosed(getBestCommonAudioPt());
|
|
|
|
super.eventExit();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|