From 2b01d06621aae3abbf58c0ddd33f5773dc6b9000 Mon Sep 17 00:00:00 2001 From: Richard Alam Date: Wed, 18 Nov 2009 19:54:37 +0000 Subject: [PATCH] - move things here and there git-svn-id: http://bigbluebutton.googlecode.com/svn/trunk@3015 af16638f-c34d-0410-8cfa-b39d5352b314 --- .../java/org/red5/app/sip/RtmpConnection.java | 41 +- .../org/red5/app/sip/SipRegisterAgent.java | 420 +++++++++++++ .../app/sip/SipRegisterAgentListener.java | 9 + .../main/java/org/red5/app/sip/SipUser.java | 179 +----- .../java/org/red5/app/sip/SipUserAgent.java | 573 +++++++----------- .../red5/app/sip/SipUserAgentListener.java | 37 +- 6 files changed, 716 insertions(+), 543 deletions(-) create mode 100644 bbb-voice/src/main/java/org/red5/app/sip/SipRegisterAgent.java create mode 100644 bbb-voice/src/main/java/org/red5/app/sip/SipRegisterAgentListener.java diff --git a/bbb-voice/src/main/java/org/red5/app/sip/RtmpConnection.java b/bbb-voice/src/main/java/org/red5/app/sip/RtmpConnection.java index e1370e3195..a7274ccf96 100644 --- a/bbb-voice/src/main/java/org/red5/app/sip/RtmpConnection.java +++ b/bbb-voice/src/main/java/org/red5/app/sip/RtmpConnection.java @@ -5,7 +5,7 @@ import org.red5.server.api.IScope; import org.red5.server.api.service.IServiceCapableConnection; import org.slf4j.Logger; -public class RtmpConnection implements ScopeProvider { +public class RtmpConnection implements ScopeProvider, SipUserAgentListener, SipRegisterAgentListener { private static Logger log = Red5LoggerFactory.getLogger( RtmpConnection.class, "sip" ); private final IServiceCapableConnection connection; @@ -20,57 +20,56 @@ public class RtmpConnection implements ScopeProvider { return scope; } - public void onCallIncoming(String source, String sourceName, String destination, String destinationName) { - connection.invoke( "incoming", - new Object[] { source, sourceName, destination, destinationName } ); + public void onNewIncomingCall(String source, String sourceName, String destination, String destinationName) { + connection.invoke("incoming", new Object[] {source, sourceName, destination, destinationName}); } - public void onUaCallRinging() { - log.debug( "onUaCallRinging" ); + public void onOutgoingCallRemoteRinging() { + log.debug("onOutgoingCallRemoteRinging"); connection.invoke("callState", new Object[] {"onUaCallRinging"}); } - public void onUaCallAccepted() { - log.debug( "onUaCallAccepted" ); + public void onOutgoingCallAccepted() { + log.debug("onOutgoingCallAccepted"); connection.invoke("callState", new Object[] {"onUaCallAccepted"}); } - public void onUaCallConnected(String playName, String publishName) { + public void onCallConnected(String playName, String publishName) { log.debug( "SIP Call Connected" ); connection.invoke("connected", new Object[] {playName, publishName}); } - public void onUaCallTrasferred() { - log.debug( "onUaCallTrasferred"); + public void onCallTrasferred() { + log.debug("onCallTrasferred"); connection.invoke("callState", new Object[] {"onUaCallTrasferred"}); } - public void onUaCallCancelled() { - log.debug( "onUaCallCancelled"); + public void onIncomingCallCancelled() { + log.debug("onIncomingCallCancelled"); connection.invoke("callState", new Object[] {"onUaCallCancelled"}); } - public void onUaCallFailed() { - log.debug( "onUaCallFailed"); - connection.invoke("callState", new Object[] {"onUaCallFailed" }); + public void onOutgoingCallFailed() { + log.debug("onOutgoingCallFailed"); + connection.invoke("callState", new Object[] {"onUaCallFailed"}); } - public void onUaCallClosed() { - log.debug( "onUaCallClosed"); + public void onCallClosed() { + log.debug("onCallClosed"); connection.invoke("callState", new Object[] {"onUaCallClosed"}); } - public void onUaRegistrationSuccess(String result) { + public void onRegistrationSuccess(String result) { log.debug( "SIP Registration success " + result ); connection.invoke("registrationSucess", new Object[] {result}); } - public void onUaRegistrationFailure(String result) { + public void onRegistrationFailure(String result) { log.debug( "SIP Registration failure " + result ); connection.invoke("registrationFailure", new Object[] {result}); } - public void onUaUnregistedSuccess() { + public void onUnregistedSuccess() { // TODO Auto-generated method stub } } diff --git a/bbb-voice/src/main/java/org/red5/app/sip/SipRegisterAgent.java b/bbb-voice/src/main/java/org/red5/app/sip/SipRegisterAgent.java new file mode 100644 index 0000000000..b886f27751 --- /dev/null +++ b/bbb-voice/src/main/java/org/red5/app/sip/SipRegisterAgent.java @@ -0,0 +1,420 @@ +package org.red5.app.sip; + +import local.net.KeepAliveSip; +import org.zoolu.net.SocketAddress; +import org.zoolu.sip.address.*; +import org.zoolu.sip.provider.SipStack; +import org.zoolu.sip.provider.SipProvider; +import org.zoolu.sip.header.*; +import org.zoolu.sip.message.*; +import org.zoolu.sip.transaction.TransactionClient; +import org.zoolu.sip.transaction.TransactionClientListener; +import org.zoolu.sip.authentication.DigestAuthentication; + +import org.slf4j.Logger; +import org.red5.logging.Red5LoggerFactory; + +import java.util.HashSet; +import java.util.Set; +import java.util.Vector; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +/** + * Register User Agent. It registers (one time or periodically) a contact + * address with a registrar server. + */ +public class SipRegisterAgent implements TransactionClientListener { + private static Logger log = Red5LoggerFactory.getLogger(SipRegisterAgent.class, "sip"); + + private final Executor exec = Executors.newSingleThreadExecutor(); + private Runnable registerProcess; + private volatile boolean continueRegistering = false; + + /** The CallerID and CSeq that should be used during REGISTER method */ + private CallIdHeader registerCallID; + private int registerCSeq; + + private static final int MAX_ATTEMPTS = 3; + + private Set listeners = new HashSet(); + + private SipProvider sipProvider; + private NameAddress target; /** User's URI with the fully qualified domain name of the registrar server. */ + private String username; + private String realm; + private String passwd; + private String nextNonce; /** Nonce for the next authentication. */ + private String qop; /** Qop for the next authentication. */ + private NameAddress contact; /** User's contact address. */ + private int expireTime; /** Expiration time. */ + private int renewTime; + private int origRenewTime; // Change by lior. + private int minRenewTime = 20; + private int regFailRetryTime = 15; + private boolean lastRegFailed = false; // Changed by Lior. + private boolean regInprocess = false; + private int attempts; /** Number of registration attempts. */ + private KeepAliveSip keepAlive; /** KeepAliveSip daemon. */ + + public SipRegisterAgent(SipProvider sipProvider, String targetUrl, String contactUrl) { + init(sipProvider, targetUrl, contactUrl); + } + + /** + * Creates a new RegisterAgent with authentication credentials (i.e. + * username, realm, and passwd). + */ + public SipRegisterAgent(SipProvider sipProvider, String targetUrl, + String contactUrl, String username, String realm, String passwd) { + + init(sipProvider, targetUrl, contactUrl); + + // Authentication. + this.username = username; + this.realm = realm; + this.passwd = passwd; + } + + private void init(SipProvider sipProvider, String targetUrl, String contactUrl) { + this.sipProvider = sipProvider; + this.target = new NameAddress(targetUrl); + this.contact = new NameAddress(contactUrl); + // this.expire_time=SipStack.default_expires; + this.expireTime = 600; + // Changed by Lior. + this.renewTime = 600; + this.origRenewTime = this.renewTime; + this.keepAlive = null; + // Authentication. + this.username = null; + this.realm = null; + this.passwd = null; + this.nextNonce = null; + this.qop = null; + this.attempts = 0; + this.minRenewTime = 20; + this.regFailRetryTime = 5; + + this.registerCallID = null; + this.registerCSeq = 0; + } + + public void addListener(SipRegisterAgentListener listener) { + listeners.add(listener); + } + + public void removeListener(SipRegisterAgentListener listener) { + listeners.remove(listener); + } + + private boolean isPeriodicallyRegistering() { + return continueRegistering; + } + + /** Registers with the registrar server. */ + public void register() { + register(expireTime); + } + + /** Registers with the registrar server for expire_time seconds. */ + public void register(int expireTime) { + attempts = 0; + lastRegFailed = false; + regInprocess = true; + + if (expireTime > 0) this.expireTime = expireTime; + + Message req = MessageFactory.createRegisterRequest(sipProvider, target, target, contact); + + /* + * MY_FIX: registerCallID contains the CallerID randomly generated in + * the first REGISTER method. It will be reused for all successive + * REGISTER invocations + */ + if (this.registerCallID == null) + this.registerCallID = req.getCallIdHeader(); + else + req.setCallIdHeader(this.registerCallID); + + /* + * MY_FIX: the registerCSeq must be unique for a given CallerID + */ + this.registerCSeq++; + req.setCSeqHeader(new CSeqHeader(this.registerCSeq, SipMethods.REGISTER)); + + req.setExpiresHeader(new ExpiresHeader(String.valueOf(expireTime))); + + if (nextNonce != null) { + AuthorizationHeader authHeader = new AuthorizationHeader("Digest"); + authHeader.addUsernameParam(username); + authHeader.addRealmParam(realm); + authHeader.addNonceParam(nextNonce); + authHeader.addUriParam(req.getRequestLine().getAddress().toString()); + authHeader.addQopParam(qop); + + String response = (new DigestAuthentication(SipMethods.REGISTER, + authHeader, null, passwd)).getResponse(); + authHeader.addResponseParam(response); + + req.setAuthorizationHeader(authHeader); + } + + if (expireTime > 0) + printLog("Registering contact " + contact + " (it expires in " + expireTime + " secs)"); + else + printLog("Unregistering contact " + contact); + + TransactionClient t = new TransactionClient(sipProvider, req, this); + t.request(); + } + + public void unregister() { + if (isPeriodicallyRegistering()) { + stopRegistering(); + } + + register(0); + sipProvider = null; + } + + public void unregisterall() { + attempts = 0; + Message req = MessageFactory.createRegisterRequest(sipProvider, target, target, null); + req.setExpiresHeader(new ExpiresHeader(String.valueOf(0))); + printLog("Unregistering all contacts"); + TransactionClient t = new TransactionClient(sipProvider, req, this); + t.request(); + } + + /** + * Periodically registers with the registrar server. + * + * @param expireTime + * expiration time in seconds + * @param renewTime + * renew time in seconds + */ + public void loopRegister(int expireTime, int renewTime) { + this.expireTime = expireTime; + this.renewTime = renewTime; + + if (!isPeriodicallyRegistering()) { + registerProcess = new Runnable() { + public void run() { + registerWithServer(); + } + }; + exec.execute(registerProcess); + } + } + + /** + * Periodically registers with the registrar server. + * + * @param expireTime + * expiration time in seconds + * @param renewTime + * renew time in seconds + * @param keepaliveTime + * keep-alive packet rate (inter-arrival time) in milliseconds + */ + public void loopRegister(int expireTime, int renewTime, long keepaliveTime) { + if (isPeriodicallyRegistering()) { + stopRegistering(); + } + + loopRegister(expireTime, renewTime); + // Keep-alive. + if (keepaliveTime > 0) { + SipURL targetUrl = target.getAddress(); + String targetHost = targetUrl.getHost(); + int targePort = targetUrl.getPort(); + if (targePort < 0) targePort = SipStack.default_port; + + SocketAddress socket = new SocketAddress(targetHost, targePort); + keepAlive = new KeepAliveSip(sipProvider, socket, null, keepaliveTime); + } + } + + public void stopRegistering() { + continueRegistering = false; + if (keepAlive != null) + keepAlive.halt(); + } + + // ***************************** run() ***************************** + + /** Run method */ + private void registerWithServer() { + continueRegistering = true; + try { + while (continueRegistering) { + register(); + // Changed by Lior. + long waitCnt = 0; + while (regInprocess) { + Thread.sleep(1000); + waitCnt += 1000; + } + + if (lastRegFailed) { + printLog("Failed Registration stop try."); + stopRegistering(); + } else + Thread.sleep(renewTime * 1000 - waitCnt); + } + } catch (Exception e) { + printException(e); + } + continueRegistering = false; + } + + // **************** Transaction callback functions ***************** + + /** Callback function called when client sends back a failure response. */ + + /** Callback function called when client sends back a provisional response. */ + public void onTransProvisionalResponse(TransactionClient transaction, Message resp) { + // do nothing... + } + + /** Callback function called when client sends back a success response. */ + public void onTransSuccessResponse(TransactionClient transaction, Message resp) { + if (transaction.getTransactionMethod().equals(SipMethods.REGISTER)) { + if (resp.hasAuthenticationInfoHeader()) { + nextNonce = resp.getAuthenticationInfoHeader().getNextnonceParam(); + } + StatusLine status = resp.getStatusLine(); + String result = status.getCode() + " " + status.getReason(); + + // Update the renew_time. + // Changed by Lior. + int expires = 0; + + if (resp.hasExpiresHeader()) { + expires = resp.getExpiresHeader().getDeltaSeconds(); + } else if (resp.hasContactHeader()) { + // Look for the max expires - should be the latest. + Vector contacts = resp.getContacts().getHeaders(); + for (int i = 0; i < contacts.size(); i++) { + int exp_i = (new ContactHeader((Header) contacts.elementAt(i))).getExpires(); + if (exp_i / 2 > expires) + expires = exp_i / 2; + } + } + + if (expires > 0 && expires < renewTime) { + renewTime = expires; + if (renewTime < minRenewTime) { + printLog("Attempt to set renew time below min renew. Attempted=" + + renewTime + " min=" + minRenewTime + "\r\nResponse=" + resp.toString()); + renewTime = minRenewTime; + } + } else if (expires > origRenewTime) { + printLog("Attempt to set renew time above original renew. Attempted=" + + expires + " origrenew=" + origRenewTime + "\r\nResponse=" + resp.toString()); + } + + printLog("Registration success: "); + regInprocess = false; + + notifyListenersOfRegistrationSuccess(result); + } + } + + private void notifyListenersOfRegistrationSuccess(String result) { + for (SipRegisterAgentListener listener : listeners) { + listener.onRegistrationSuccess(result); + } + } + + /** Callback function called when client sends back a failure response. */ + public void onTransFailureResponse(TransactionClient transaction, Message resp) { + printLog("onTransFailureResponse start: "); + + if (transaction.getTransactionMethod().equals(SipMethods.REGISTER)) { + StatusLine status = resp.getStatusLine(); + int code = status.getCode(); + if ((code == 401 && attempts < MAX_ATTEMPTS && resp.hasWwwAuthenticateHeader() + && resp.getWwwAuthenticateHeader().getRealmParam().equalsIgnoreCase(realm)) + || (code == 407 && attempts < MAX_ATTEMPTS && resp.hasProxyAuthenticateHeader() + && resp.getProxyAuthenticateHeader().getRealmParam().equalsIgnoreCase(realm))) + { + printLog("onTransFailureResponse 401 or 407: "); + + attempts++; + Message req = transaction.getRequestMessage(); + req.setCSeqHeader(req.getCSeqHeader().incSequenceNumber()); + // * MY_FIX: registerCSeq counter must incremented. + this.registerCSeq++; + + WwwAuthenticateHeader wah; + if (code == 401) + wah = resp.getWwwAuthenticateHeader(); + else + wah = resp.getProxyAuthenticateHeader(); + + String qopOptions = wah.getQopOptionsParam(); + // qop=(qopOptions!=null)? "auth" : null; + + // Select a new branch - rfc3261 says should be new on each + // request. + ViaHeader via = req.getViaHeader(); + req.removeViaHeader(); + via.setBranch(SipProvider.pickBranch()); + req.addViaHeader(via); + qop = (qopOptions != null) ? "auth" : null; + + DigestAuthentication digest = new DigestAuthentication(SipMethods.REGISTER, + req.getRequestLine().getAddress().toString(), wah, qop, null, username, passwd); + + AuthorizationHeader ah; + if (code == 401) + ah = digest.getAuthorizationHeader(); + else + ah = digest.getProxyAuthorizationHeader(); + + req.setAuthorizationHeader(ah); + TransactionClient t = new TransactionClient(sipProvider, req, this); + t.request(); + + } else { + String result = code + " " + status.getReason(); + lastRegFailed = true; + regInprocess = false; + + printLog("Registration failure: " + result); + notifyListenersOfRegistrationFailure(result); + } + } + } + + private void notifyListenersOfRegistrationFailure(String result) { + for (SipRegisterAgentListener listener : listeners) { + listener.onRegistrationFailure(result); + } + } + + /** Callback function called when client expires timeout. */ + public void onTransTimeout(TransactionClient transaction) { + if (transaction.getTransactionMethod().equals(SipMethods.REGISTER)) { + lastRegFailed = true; + regInprocess = false; + + printLog("Registration failure: No response from server."); + notifyListenersOfRegistrationFailure( "Timeout"); + } + } + + // ****************************** Logs ***************************** + + void printLog(String str) { + System.out.println("RegisterAgent: " + str); + } + + void printException(Exception e) { + System.out.println("RegisterAgent Exception: " + e); + } + +} diff --git a/bbb-voice/src/main/java/org/red5/app/sip/SipRegisterAgentListener.java b/bbb-voice/src/main/java/org/red5/app/sip/SipRegisterAgentListener.java new file mode 100644 index 0000000000..fe4688d3f2 --- /dev/null +++ b/bbb-voice/src/main/java/org/red5/app/sip/SipRegisterAgentListener.java @@ -0,0 +1,9 @@ +package org.red5.app.sip; + +public interface SipRegisterAgentListener { + /** When a UA has been successfully (un)registered. */ + public void onRegistrationSuccess(String result); + /** When a UA failed on (un)registering. */ + public void onRegistrationFailure(String result); + public void onUnregistedSuccess(); +} diff --git a/bbb-voice/src/main/java/org/red5/app/sip/SipUser.java b/bbb-voice/src/main/java/org/red5/app/sip/SipUser.java index 7089ee7c22..d4c6b5c060 100644 --- a/bbb-voice/src/main/java/org/red5/app/sip/SipUser.java +++ b/bbb-voice/src/main/java/org/red5/app/sip/SipUser.java @@ -1,12 +1,11 @@ package org.red5.app.sip; -import org.zoolu.sip.address.*; import org.zoolu.sip.provider.*; import org.zoolu.net.SocketAddress; import org.slf4j.Logger; import org.red5.logging.Red5LoggerFactory; -public class SipUser implements SipUserAgentListener, RegisterAgentListener { +public class SipUser { private static Logger log = Red5LoggerFactory.getLogger(SipUser.class, "sip"); private RtmpConnection rtmpConnection; @@ -16,58 +15,33 @@ public class SipUser implements SipUserAgentListener, RegisterAgentListener { private SipProvider sipProvider; private String optOutboundProxy = null; private SipUserAgent userAgent; - private RegisterAgent registerAgent; - private RTMPUser rtmpUser; - private String username; - private String password; - private String publishName; - private String playName; - private int sipPort; - private int rtpPort; + private SipRegisterAgent registerAgent; private String proxy; - private String realm; public SipUser(String userid, RtmpConnection connection, int sipPort, int rtpPort) { log.debug( "SIPUser Constructor: sip port " + sipPort + " rtp port:" + rtpPort ); this.userid = userid; this.rtmpConnection = connection; - this.sipPort = sipPort; - this.rtpPort = rtpPort; + + initializeSipStack(); + initializeSipProvider(sipPort); + initializeUserProfile(rtpPort); } -/* - public boolean isRunning() { - boolean resp = false; - try { - resp = userAgent.isReceiverRunning(); - } - catch ( Exception e ) { - resp = false; - } - - return resp; - } -*/ public void login(String obproxy, String phone, String username, String password, String realm, String proxy) { log.debug( "SIPUser login" ); - - this.username = username; - this.password = password; this.proxy = proxy; this.optOutboundProxy = obproxy; - this.realm = realm; - rtmpUser = new RTMPUser(); - - initializeSipStack(); - initializeSipProvider(); - initializeUserProfile(phone); + sipProvider.setOutboundProxy(new SocketAddress(optOutboundProxy)); + setupUserProfile(username, password, realm, phone); initializeUserAgent(); } private void initializeUserAgent() { - userAgent = new SipUserAgent(sipProvider, userProfile, this, rtmpConnection); - userAgent.waitForIncomingCalls(); + userAgent = new SipUserAgent(sipProvider, userProfile, rtmpConnection); + userAgent.addListener(rtmpConnection); + userAgent.initialize(); } private void initializeSipStack() { @@ -76,23 +50,24 @@ public class SipUser implements SipUserAgentListener, RegisterAgentListener { SipStack.log_path = "log"; } - private void initializeSipProvider() { - sipProvider = new SipProvider( null, sipPort ); - sipProvider.setOutboundProxy(new SocketAddress(optOutboundProxy)); + private void initializeSipProvider(int sipPort) { + sipProvider = new SipProvider(null, sipPort); sipProvider.addSipProviderListener(new OptionMethodListener()); } - private void initializeUserProfile(String phone) { + private void setupUserProfile(String username, String password, String realm, String phone) { String fromURL = "\"" + phone + "\" "; - - userProfile = new SipUserAgentProfile(); - userProfile.audioPort = rtpPort; - userProfile.username = username; + userProfile.username = username; userProfile.passwd = password; userProfile.realm = realm; userProfile.fromUrl = fromURL; userProfile.contactUrl = "sip:" + phone + "@" + sipProvider.getViaAddress(); - + } + + private void initializeUserProfile(int rtpPort) { + userProfile = new SipUserAgentProfile(); + userProfile.audioPort = rtpPort; + if ( sipProvider.getPort() != SipStack.default_port ) { userProfile.contactUrl += ":" + sipProvider.getPort(); } @@ -105,9 +80,10 @@ public class SipUser implements SipUserAgentListener, RegisterAgentListener { public void register() { log.debug( "SIPUser register" ); if (sipProvider != null) { - registerAgent = new RegisterAgent( sipProvider, userProfile.fromUrl, userProfile.contactUrl, username, - userProfile.realm, password, this ); - loopRegister(userProfile.expires, userProfile.expires / 2, userProfile.keepaliveTime); + registerAgent = new SipRegisterAgent(sipProvider, userProfile.fromUrl, userProfile.contactUrl, + userProfile.username, userProfile.realm, userProfile.passwd); + registerAgent.addListener(rtmpConnection); + loopRegister(userProfile.expires, userProfile.expires/2, userProfile.keepaliveTime); } } @@ -148,7 +124,7 @@ public class SipUser implements SipUserAgentListener, RegisterAgentListener { public void close() { log.debug("SIPUser close1"); - try { + try { hangup(); unregister(); new Thread().sleep(3000); @@ -156,41 +132,26 @@ public class SipUser implements SipUserAgentListener, RegisterAgentListener { log.error("close: Exception:>\n" + e); } - try { - log.debug("SIPUser provider.halt"); - sipProvider.halt(); - - } catch(Exception e) { - log.error("close: Exception:>\n" + e); - } - rtmpConnection = null; + log.debug("Stopping SipProvider"); + sipProvider.halt(); } public void accept() { log.debug( "SIPUser accept" ); if (userAgent != null) { - try { - userAgent.accept(); - } - catch ( Exception e ) { - log.error( "SIPUser: accept - Exception:>\n" + e ); - } + userAgent.accept(); } } public void hangup() { log.debug( "SIPUser hangup" ); - if ( userAgent != null ) { - if ( userAgent.call_state != UserAgent.UA_IDLE ) { - userAgent.hangup(); - userAgent.waitForIncomingCalls(); - } + if (userAgent != null) { + userAgent.hangup(); } closeStreams(); - rtmpUser.stopStream(); } public void streamStatus( String status ) { @@ -204,15 +165,12 @@ public class SipUser implements SipUserAgentListener, RegisterAgentListener { public void unregister() { log.debug( "SIPUser unregister" ); - if ( registerAgent != null ) { - if ( registerAgent.isRegistering() ) { - registerAgent.halt(); - } + if (registerAgent != null) { registerAgent.unregister(); registerAgent = null; } - if ( userAgent != null ) { + if (userAgent != null) { userAgent.hangup(); } userAgent = null; @@ -247,77 +205,6 @@ public class SipUser implements SipUserAgentListener, RegisterAgentListener { } private void loopRegister( int expire_time, int renew_time, long keepalive_time ) { - if ( registerAgent.isRegistering() ) { - registerAgent.halt(); - } registerAgent.loopRegister( expire_time, renew_time, keepalive_time ); } - - public void onUaCallIncoming( SipUserAgent ua, NameAddress callee, NameAddress caller ) { - String source = caller.getAddress().toString(); - String sourceName = caller.hasDisplayName() ? caller.getDisplayName() : ""; - String destination = callee.getAddress().toString(); - String destinationName = callee.hasDisplayName() ? callee.getDisplayName() : ""; - - log.debug( "onUaCallIncoming " + source + " " + destination); - rtmpConnection.onCallIncoming(source, sourceName, destination, destinationName); - } - - public void onUaCallRinging( SipUserAgent ua ) { - log.debug( "onUaCallRinging" ); - rtmpConnection.onUaCallRinging(); - } - - public void onUaCallAccepted( SipUserAgent ua ) { - log.debug( "onUaCallAccepted" ); - rtmpConnection.onUaCallAccepted(); - } - - public void onUaCallConnected(SipUserAgent ua, String playName, String publishName) { - log.debug( "SIP Call Connected" ); - rtmpConnection.onUaCallConnected(playName, publishName); - } - - public void onUaCallTrasferred( SipUserAgent ua ) { - log.debug( "onUaCallTrasferred"); - rtmpConnection.onUaCallTrasferred(); - } - - public void onUaCallCancelled( SipUserAgent ua ) { - log.debug( "onUaCallCancelled"); - closeStreams(); - rtmpConnection.onUaCallCancelled(); - ua.waitForIncomingCalls(); - } - - public void onUaCallFailed( SipUserAgent ua ) { - log.debug( "onUaCallFailed"); - closeStreams(); - rtmpConnection.onUaCallFailed(); - ua.waitForIncomingCalls(); - } - - - public void onUaCallClosed( SipUserAgent ua ) { - log.debug( "onUaCallClosed"); - closeStreams(); - rtmpConnection.onUaCallClosed(); - ua.waitForIncomingCalls(); - } - - public void onUaRegistrationSuccess( RegisterAgent ra, NameAddress target, NameAddress contact, String result ) { - log.debug( "SIP Registration success " + result ); - rtmpConnection.onUaRegistrationSuccess(result); - } - - - public void onUaRegistrationFailure( RegisterAgent ra, NameAddress target, NameAddress contact, String result ) { - log.debug( "SIP Registration failure " + result ); - rtmpConnection.onUaRegistrationFailure(result); - } - - - public void onUaUnregistedSuccess() { - // TODO Auto-generated method stub - } } diff --git a/bbb-voice/src/main/java/org/red5/app/sip/SipUserAgent.java b/bbb-voice/src/main/java/org/red5/app/sip/SipUserAgent.java index c7133abded..94469e11f4 100644 --- a/bbb-voice/src/main/java/org/red5/app/sip/SipUserAgent.java +++ b/bbb-voice/src/main/java/org/red5/app/sip/SipUserAgent.java @@ -12,6 +12,8 @@ import org.slf4j.Logger; import org.red5.logging.Red5LoggerFactory; import org.zoolu.tools.Parser; import java.util.Enumeration; +import java.util.HashSet; +import java.util.Set; import java.util.Vector; public class SipUserAgent extends CallListenerAdapter { @@ -23,136 +25,66 @@ public class SipUserAgent extends CallListenerAdapter { private ExtendedCall callTransfer; private CallStream callStream; private String localSession = null; - private SipUserAgentListener listener = null; private Codec sipCodec = null; private final ScopeProvider scopeProvider; + private Set listeners = new HashSet(); - private static final String UA_IDLE = "IDLE"; /** UA_IDLE=0 */ - private static final String UA_INCOMING_CALL = "INCOMING_CALL"; /** UA_INCOMING_CALL=1 */ - private static final String UA_OUTGOING_CALL = "OUTGOING_CALL"; /** UA_OUTGOING_CALL=2 */ - private static final String UA_ONCALL = "ONCALL"; /** UA_ONCALL=3 */ - - /** - * Call state - *

- * UA_IDLE=0,
- * UA_INCOMING_CALL=1,
- * UA_OUTGOING_CALL=2,
- * UA_ONCALL=3 - */ - String call_state = UA_IDLE; - - protected void changeStatus(String state) { - call_state = state; + private enum CallState { + UA_IDLE(0), UA_INCOMING_CALL(1), UA_OUTGOING_CALL(2), UA_ONCALL(3); + private final int state; + CallState(int state) {this.state = state;} + private int getState() {return state;} } - protected boolean statusIs(String state) { - return call_state.equals(state); + private CallState callState; + + public SipUserAgent(SipProvider sipProvider, SipUserAgentProfile userProfile, ScopeProvider scopeProvider) { + this.scopeProvider = scopeProvider; + this.sipProvider = sipProvider; + this.userProfile = userProfile; + + // If no contact_url and/or from_url has been set, create it now. + userProfile.initContactAddress(sipProvider); + // Set local sdp. + initSessionDescriptor(); } - - protected String getStatus() { - return call_state; + + public void addListener(SipUserAgentListener listener) { + listeners.add(listener); + } + + public void removeListener(SipUserAgentListener listener) { + listeners.remove(listener); + } + + private void changeStatus(CallState state) { + callState = state; } - - - /** - * Sets the automatic answer time (default is -1 that means no auto accept - * mode) - */ - public void setAcceptTime(int accept_time) { - userProfile.acceptTime = accept_time; + + public boolean isIdle() { + return callState == CallState.UA_IDLE; } - - - /** - * Sets the automatic hangup time (default is 0, that corresponds to manual - * hangup mode) - */ - public void setHangupTime(int time) { - userProfile.hangupTime = time; - } - - - /** Sets the redirection url (default is null, that is no redircetion) */ - public void setRedirection(String url) { - userProfile.redirectTo = url; - } - - /** Sets the no offer mode for the invite (default is false) */ - public void setNoOfferMode(boolean nooffer) { - userProfile.noOffer = nooffer; - } - - public void setAudio(boolean enable) { - userProfile.audio = enable; - } - - public void setVideo(boolean enable) { - userProfile.video = enable; - } - - public void setReceiveOnlyMode(boolean r_only) { - userProfile.recvOnly = r_only; - } - - public void setSendOnlyMode(boolean s_only) { - userProfile.sendOnly = s_only; - } - - public void setSendToneMode(boolean s_tone) { - userProfile.sendTone = s_tone; - } - - public void setSendFile(String file_name) { - userProfile.sendFile = file_name; - } - - public void setRecvFile(String file_name) { - userProfile.recvFile = file_name; - } - - public String getSessionDescriptor() { - return localSession; - } - - public void setSessionDescriptor(String sdp) { - localSession = sdp; - } - -// public boolean isReceiverRunning() { -// return callStream.isReceiverRunning(); -// } public void queueSipDtmfDigits(String digits) { callStream.queueSipDtmfDigits(digits); } + public void initialize() { + waitForIncomingCalls(); + } + public void initSessionDescriptor() { log.debug("initSessionDescriptor"); - SessionDescriptor newSdp = SdpUtils.createInitialSdp( - userProfile.username, sipProvider.getViaAddress(), - userProfile.audioPort, userProfile.videoPort, - userProfile.audioCodecsPrecedence ); + SessionDescriptor newSdp = SdpUtils.createInitialSdp(userProfile.username, + sipProvider.getViaAddress(), userProfile.audioPort, + userProfile.videoPort, userProfile.audioCodecsPrecedence ); localSession = newSdp.toString(); log.debug("localSession Descriptor = " + localSession ); } - public SipUserAgent(SipProvider sipProvider, SipUserAgentProfile userProfile, - SipUserAgentListener listener, ScopeProvider scopeProvider) { - this.scopeProvider = scopeProvider; - this.sipProvider = sipProvider; - this.listener = listener; - this.userProfile = userProfile; - - // If no contact_url and/or from_url has been set, create it now. - userProfile.initContactAddress( sipProvider ); - // Set local sdp. - initSessionDescriptor(); - } - public void call(String targetUrl) { - log.debug( "call", "Init..." ); - changeStatus( UA_OUTGOING_CALL ); + log.debug( "call", "Init..." ); + changeStatus(CallState.UA_OUTGOING_CALL); call = new ExtendedCall(sipProvider, userProfile.fromUrl, userProfile.contactUrl, userProfile.username, @@ -165,7 +97,7 @@ public class SipUserAgent extends CallListenerAdapter { if (userProfile.noOffer) { call.call(targetUrl); } else { - call.call( targetUrl, localSession ); + call.call(targetUrl, localSession); } } @@ -180,9 +112,9 @@ public class SipUserAgent extends CallListenerAdapter { /** Waits for an incoming call (acting as UAS). */ - public void waitForIncomingCalls() { - log.debug("waitForIncomingCalls..." ); - changeStatus( UA_IDLE ); + private void waitForIncomingCalls() { + log.debug("waitForIncomingCalls..." ); + changeStatus(CallState.UA_IDLE); call = new ExtendedCall(sipProvider, userProfile.fromUrl, userProfile.contactUrl, userProfile.username, @@ -194,12 +126,13 @@ public class SipUserAgent extends CallListenerAdapter { /** Closes an ongoing, incoming, or pending call */ public void hangup() { log.debug("hangup"); - - closeMediaApplication(); - if (call != null) { - call.hangup(); - } - changeStatus(UA_IDLE); + + if (isIdle()) return; + + closeMediaApplication(); + if (call != null) call.hangup(); + changeStatus(CallState.UA_IDLE); + waitForIncomingCalls(); } @@ -212,8 +145,6 @@ public class SipUserAgent extends CallListenerAdapter { } } - - /** Redirects an incoming call */ public void redirect(String redirection) { log.debug( "redirect", "Init..." ); @@ -243,9 +174,7 @@ public class SipUserAgent extends CallListenerAdapter { try { callStream = new CallStream(sipCodec, connInfo, scopeProvider); - if (listener != null) { - listener.onUaCallConnected(this, callStream.getListenStreamName(), callStream.getTalkStreamName()); - } + notifyListenersOnCallConnected(callStream.getListenStreamName(), callStream.getTalkStreamName()); } catch (Exception e) { log.error("Failed to create Call Stream."); } @@ -253,9 +182,15 @@ public class SipUserAgent extends CallListenerAdapter { } } + private void notifyListenersOnCallConnected(String listenStream, String talkStream) { + for (SipUserAgentListener listener : listeners) { + listener.onCallConnected(listenStream, talkStream); + } + } + private int getLocalAudioPort(SessionDescriptor localSdp) { int localAudioPort = 0; - int localVideoPort = 0; + //int localVideoPort = 0; // parse local sdp for ( Enumeration e = localSdp.getMediaDescriptors().elements(); e.hasMoreElements(); ) { @@ -263,9 +198,9 @@ public class SipUserAgent extends CallListenerAdapter { if ( media.getMedia().equals( "audio" ) ) { localAudioPort = media.getPort(); } - if ( media.getMedia().equals( "video" ) ) { - localVideoPort = media.getPort(); - } + //if ( media.getMedia().equals( "video" ) ) { + // localVideoPort = media.getPort(); + //} } return localAudioPort; @@ -273,9 +208,9 @@ public class SipUserAgent extends CallListenerAdapter { private int getRemoteAudioPort(SessionDescriptor remoteSdp) { int remoteAudioPort = 0; - int remoteVideoPort = 0; + //int remoteVideoPort = 0; - for ( Enumeration e = remoteSdp.getMediaDescriptors().elements(); e.hasMoreElements(); ) { + for (Enumeration e = remoteSdp.getMediaDescriptors().elements(); e.hasMoreElements();) { MediaDescriptor descriptor = (MediaDescriptor) e.nextElement(); MediaField media = descriptor.getMedia(); @@ -283,9 +218,9 @@ public class SipUserAgent extends CallListenerAdapter { remoteAudioPort = media.getPort(); } - if ( media.getMedia().equals( "video" ) ) { - remoteVideoPort = media.getPort(); - } + // if ( media.getMedia().equals( "video" ) ) { + // remoteVideoPort = media.getPort(); + // } } return remoteAudioPort; @@ -296,7 +231,7 @@ public class SipUserAgent extends CallListenerAdapter { } protected void closeMediaApplication() { - log.debug( "closeMediaApplication", "Init..." ); + log.debug("closeMediaApplication" ); if (callStream != null) { callStream.stopMedia(); @@ -307,125 +242,41 @@ public class SipUserAgent extends CallListenerAdapter { // ********************** Call callback functions ********************** + private void createAudioCodec(SessionDescriptor newSdp) { + sipCodec = SdpUtils.getNegotiatedAudioCodec(newSdp); + } + /** * Callback function called when arriving a new INVITE method (incoming call) */ public void onCallIncoming(Call call, NameAddress callee, NameAddress caller, String sdp, Message invite) { log.debug("onCallIncoming"); - if (call != this.call) { - log.debug("NOT the current call."); - return; - } + if (!isCurrentCall(call)) return; - log.debug("Inside SIPUserAgent.onCallIncoming(): sdp=\n" + sdp); + log.debug("IncomingCallIncoming()"); - changeStatus(UA_INCOMING_CALL); + changeStatus(CallState.UA_INCOMING_CALL); call.ring(); if (sdp != null) { - SessionDescriptor remoteSdp = new SessionDescriptor(sdp); - SessionDescriptor localSdp = new SessionDescriptor(localSession); - - log.debug("localSdp = " + localSdp.toString() + "."); - log.debug("remoteSdp = " + remoteSdp.toString() + "."); - - // First we need to make payloads negotiation so the related - // attributes can be then matched. - SessionDescriptor newSdp = SdpUtils.makeMediaPayloadsNegotiation(localSdp, remoteSdp); - - // After we can create the correct audio codec considering - // audio negotiation made above. - sipCodec = SdpUtils.getNegotiatedAudioCodec(newSdp); - - // Now we complete the SDP negotiation informing the selected - // codec, so it can be internally updated during the process. - SdpUtils.completeSdpNegotiation(newSdp, localSdp, remoteSdp); - - localSession = newSdp.toString(); - - log.debug("newSdp = " + localSession + "." ); - - // Finally, we use the "newSdp" and "remoteSdp" to initialize - // the lasting codec informations. - CodecUtils.initSipAudioCodec(sipCodec, userProfile.audioDefaultPacketization, - userProfile.audioDefaultPacketization, newSdp, remoteSdp); + setupSdpAndCodec(sdp); } - if (listener != null) { - listener.onUaCallIncoming(this, callee, caller); - } + notifyListenersOfNewIncomingCall(callee, caller); } - - - /** - * Callback function called when arriving a new Re-INVITE method - * (re-inviting/call modify) - */ - public void onCallModifying(Call call, String sdp, Message invite) { - log.debug("onCallModifying"); - - if (call != this.call) { - log.debug("NOT the current call."); - return; - } - - log.debug("RE-INVITE/MODIFY."); - - // to be implemented. - // currently it simply accepts the session changes (see method - // onCallModifying() in CallListenerAdapter) - super.onCallModifying( call, sdp, invite ); - } - - - /** - * Callback function that may be overloaded (extended). Called when arriving - * a 180 Ringing - */ - public void onCallRinging(Call call, Message resp) { - log.debug("onCallRinging"); - - if (call != this.call && call != callTransfer) { - log.debug("NOT the current call."); - return; - } - - log.debug("RINGING." ); - - // Play "on" sound. - if (listener != null) { - listener.onUaCallRinging(this); - } - } - - - /** Callback function called when arriving a 2xx (call accepted) */ - public void onCallAccepted(Call call, String sdp, Message resp) { - log.debug( "onCallAccepted"); - - if (call != this.call && call != callTransfer) { - log.debug("NOT the current call." ); - return; - } - - log.debug("ACCEPTED/CALL."); - - changeStatus(UA_ONCALL); - - SessionDescriptor remoteSdp = new SessionDescriptor(sdp); + + private void setupSdpAndCodec(String sdp) { + SessionDescriptor remoteSdp = new SessionDescriptor(sdp); SessionDescriptor localSdp = new SessionDescriptor(localSession); log.debug("localSdp = " + localSdp.toString() + "."); log.debug("remoteSdp = " + remoteSdp.toString() + "."); - // First we need to make payloads negotiation so the related - // attributes can be then matched. + // First we need to make payloads negotiation so the related attributes can be then matched. SessionDescriptor newSdp = SdpUtils.makeMediaPayloadsNegotiation(localSdp, remoteSdp); - // After we can create the correct audio codec considering - // audio negotiation made above. - sipCodec = SdpUtils.getNegotiatedAudioCodec(newSdp); + createAudioCodec(newSdp); // Now we complete the SDP negotiation informing the selected // codec, so it can be internally updated during the process. @@ -433,12 +284,71 @@ public class SipUserAgent extends CallListenerAdapter { localSession = newSdp.toString(); - log.debug("newSdp = " + localSession + "."); + log.debug("newSdp = " + localSession + "." ); - // Finally, we use the "newSdp" and "remoteSdp" to initialize - // the lasting codec informations. + // Finally, we use the "newSdp" and "remoteSdp" to initialize the lasting codec informations. CodecUtils.initSipAudioCodec(sipCodec, userProfile.audioDefaultPacketization, userProfile.audioDefaultPacketization, newSdp, remoteSdp); + } + + private void notifyListenersOfNewIncomingCall(NameAddress callee, NameAddress caller) { + String source = caller.getAddress().toString(); + String sourceName = caller.hasDisplayName() ? caller.getDisplayName() : ""; + String destination = callee.getAddress().toString(); + String destinationName = callee.hasDisplayName() ? callee.getDisplayName() : ""; + + for (SipUserAgentListener listener : listeners) { + listener.onNewIncomingCall(source, sourceName, destination, destinationName); + } + } + + /** + * Callback function called when arriving a new Re-INVITE method (re-inviting/call modify) + */ + public void onCallModifying(Call call, String sdp, Message invite) { + log.debug("onCallModifying"); + + if (!isCurrentCall(call)) return; + + log.debug("RE-INVITE/MODIFY."); + + // to be implemented. + // currently it simply accepts the session changes (see method + // onCallModifying() in CallListenerAdapter) + super.onCallModifying(call, sdp, invite); + } + + + /** + * Callback function that may be overloaded (extended). Called when arriving a 180 Ringing + */ + public void onCallRinging(Call call, Message resp) { + log.debug("onCallRinging"); + + if (!isCurrentCallOrCallTransfer(call)) return; + + log.debug("RINGING." ); + + notifyListenersOfOnOutgoingCallRemoteRinging(); + } + + private void notifyListenersOfOnOutgoingCallRemoteRinging() { + for (SipUserAgentListener listener : listeners) { + listener.onOutgoingCallRemoteRinging(); + } + } + + + /** Callback function called when arriving a 2xx (call accepted) */ + public void onCallAccepted(Call call, String sdp, Message resp) { + log.debug( "onCallAccepted"); + + if (!isCurrentCallOrCallTransfer(call)) return; + + log.debug("ACCEPTED/CALL."); + changeStatus(CallState.UA_ONCALL); + + setupSdpAndCodec(sdp); if (userProfile.noOffer) { // Answer with the local sdp. @@ -454,24 +364,24 @@ public class SipUserAgent extends CallListenerAdapter { this.call.notify(code, reason); } - if (listener != null) { - listener.onUaCallAccepted(this); - } + notifyListenersOfOnOutgoingCallAccepted(); } + public void notifyListenersOfOnOutgoingCallAccepted() { + for (SipUserAgentListener listener : listeners) { + listener.onOutgoingCallAccepted(); + } + } /** Callback function called when arriving an ACK method (call confirmed) */ public void onCallConfirmed(Call call, String sdp, Message ack) { log.debug("onCallConfirmed"); - if (call != this.call) { - log.debug("NOT the current call."); - return; - } + if (!isCurrentCall(call)) return; log.debug("CONFIRMED/CALL."); - - changeStatus(UA_ONCALL); + changeStatus(CallState.UA_ONCALL); + launchMediaApplication(); } @@ -480,10 +390,7 @@ public class SipUserAgent extends CallListenerAdapter { public void onCallReInviteAccepted(Call call, String sdp, Message resp) { log.debug( "onCallReInviteAccepted"); - if (call != this.call) { - log.debug("NOT the current call." ); - return; - } + if (!isCurrentCall(call)) return; log.debug("RE-INVITE-ACCEPTED/CALL." ); } @@ -493,30 +400,22 @@ public class SipUserAgent extends CallListenerAdapter { public void onCallReInviteRefused(Call call, String reason, Message resp) { log.debug("onCallReInviteRefused"); - if (call != this.call) { - log.debug("NOT the current call"); - return; - } + if (!isCurrentCall(call)) return; log.debug("RE-INVITE-REFUSED (" + reason + ")/CALL."); - if (listener != null) { - listener.onUaCallFailed(this); - } + notifyListenersOnOutgoingCallFailed(); + waitForIncomingCalls(); } /** Callback function called when arriving a 4xx (call failure) */ public void onCallRefused(Call call, String reason, Message resp) { log.debug("onCallRefused"); - if (call != this.call) { - log.debug("NOT the current call."); - return; - } - + if (!isCurrentCall(call)) return; + log.debug("REFUSED (" + reason + ")."); - - changeStatus(UA_IDLE); + changeStatus(CallState.UA_IDLE); if (call == callTransfer) { StatusLine status_line = resp.getStatusLine(); @@ -526,9 +425,14 @@ public class SipUserAgent extends CallListenerAdapter { callTransfer = null; } - if (listener != null) { - listener.onUaCallFailed(this); - } + notifyListenersOnOutgoingCallFailed(); + waitForIncomingCalls(); + } + + private void notifyListenersOnOutgoingCallFailed() { + for (SipUserAgentListener listener : listeners) { + listener.onOutgoingCallFailed(); + } } @@ -536,11 +440,7 @@ public class SipUserAgent extends CallListenerAdapter { public void onCallRedirection(Call call, String reason, Vector contact_list, Message resp) { log.debug("onCallRedirection"); - if (call != this.call) { - log.debug("NOT the current call."); - return; - } - + if (!isCurrentCall(call)) return; log.debug("REDIRECTION (" + reason + ")." ); call.call(((String) contact_list.elementAt(0))); @@ -548,24 +448,23 @@ public class SipUserAgent extends CallListenerAdapter { /** - * Callback function that may be overloaded (extended). Called when arriving - * a CANCEL request + * Callback function that may be overloaded (extended). Called when arriving a CANCEL request */ public void onCallCanceling(Call call, Message cancel) { log.debug("onCallCanceling"); - if (call != this.call) { - log.debug("NOT the current call."); - return; - } + if (!isCurrentCall(call)) return; log.debug("CANCEL."); - - changeStatus(UA_IDLE); - - if (listener != null) { - listener.onUaCallCancelled(this); - } + changeStatus(CallState.UA_IDLE); + notifyListenersOfOnIncomingCallCancelled(); + waitForIncomingCalls(); + } + + private void notifyListenersOfOnIncomingCallCancelled() { + for (SipUserAgentListener listener : listeners) { + listener.onIncomingCallCancelled(); + } } @@ -573,10 +472,7 @@ public class SipUserAgent extends CallListenerAdapter { public void onCallClosing(Call call, Message bye) { log.debug("onCallClosing"); - if (call != this.call && call != callTransfer) { - log.debug("NOT the current call."); - return; - } + if (!isCurrentCallOrCallTransfer(call)) return; if (call != callTransfer && callTransfer != null) { log.debug("CLOSE PREVIOUS CALL."); @@ -589,16 +485,19 @@ public class SipUserAgent extends CallListenerAdapter { closeMediaApplication(); - if (listener != null) { - listener.onUaCallClosed(this); - } - - changeStatus(UA_IDLE); + notifyListenersOfOnCallClosed(); + changeStatus(CallState.UA_IDLE); // Reset local sdp for next call. initSessionDescriptor(); + waitForIncomingCalls(); } + private void notifyListenersOfOnCallClosed() { + for (SipUserAgentListener listener : listeners) { + listener.onCallClosed(); + } + } /** * Callback function called when arriving a response after a BYE request @@ -607,18 +506,13 @@ public class SipUserAgent extends CallListenerAdapter { public void onCallClosed(Call call, Message resp) { log.debug("onCallClosed"); - if (call != this.call) { - log.debug("NOT the current call." ); - return; - } + if (!isCurrentCall(call)) return; log.debug("CLOSE/OK."); - if (listener != null) { - listener.onUaCallClosed(this); - } - - changeStatus(UA_IDLE); + notifyListenersOfOnCallClosed(); + changeStatus(CallState.UA_IDLE); + waitForIncomingCalls(); } @@ -626,40 +520,31 @@ public class SipUserAgent extends CallListenerAdapter { public void onCallTimeout(Call call) { log.debug("onCallTimeout"); - if (call != this.call) { - log.debug("NOT the current call."); - return; - } + if (!isCurrentCall(call)) return; log.debug("NOT FOUND/TIMEOUT."); - - changeStatus(UA_IDLE); + changeStatus(CallState.UA_IDLE); if (call == callTransfer) { int code = 408; String reason = "Request Timeout"; - this.call.notify( code, reason ); + this.call.notify(code, reason); callTransfer = null; } - if (listener != null) { - listener.onUaCallFailed(this); - } + notifyListenersOnOutgoingCallFailed(); + waitForIncomingCalls(); } // ****************** ExtendedCall callback functions ****************** - /** * Callback function called when arriving a new REFER method (transfer request) */ public void onCallTransfer(ExtendedCall call, NameAddress refer_to, NameAddress refered_by, Message refer) { log.debug("onCallTransfer"); - if (call != this.call) { - log.debug("NOT the current call."); - return; - } + if (!isCurrentCall(call)) return; log.debug("Transfer to " + refer_to.toString() + "."); @@ -674,10 +559,7 @@ public class SipUserAgent extends CallListenerAdapter { public void onCallTransferAccepted(ExtendedCall call, Message resp) { log.debug("onCallTransferAccepted"); - if (call != this.call) { - log.debug("NOT the current call."); - return; - } + if (!isCurrentCall(call)) return; log.debug("Transfer accepted."); } @@ -685,48 +567,45 @@ public class SipUserAgent extends CallListenerAdapter { /** Callback function called when a call transfer is refused. */ public void onCallTransferRefused(ExtendedCall call, String reason, Message resp) { - log.debug("onCallTransferRefused"); - - if (call != this.call) { - log.debug("NOT the current call."); - return; - } - - log.debug( "onCallTransferRefused", "Transfer refused." ); + log.debug("onCallTransferRefused"); + if (!isCurrentCall(call)) return; + log.debug("Transfer refused."); } /** Callback function called when a call transfer is successfully completed */ public void onCallTransferSuccess(ExtendedCall call, Message notify) { - log.debug( "onCallTransferSuccess", "Init..." ); + log.debug("onCallTransferSuccess"); + if (!isCurrentCall(call)) return; - if (call != this.call) { - log.debug("onCallTransferSuccess", "NOT the current call."); - return; - } - - log.debug( "onCallTransferSuccess", "Transfer successed." ); + log.debug("Transfer succeeded."); call.hangup(); - if ( listener != null ) { - listener.onUaCallTrasferred( this ); - } + notifyListenersOfOnCallTransferred(); } + private void notifyListenersOfOnCallTransferred() { + for (SipUserAgentListener listener : listeners) { + listener.onCallTrasferred(); + } + } /** - * Callback function called when a call transfer is NOT sucessfully - * completed + * Callback function called when a call transfer is NOT successfully completed */ public void onCallTransferFailure(ExtendedCall call, String reason, Message notify) { - log.debug( "onCallTransferFailure", "Init..." ); + log.debug("onCallTransferFailure"); + if (!isCurrentCall(call)) return; - if ( call != this.call ) { - log.debug( "onCallTransferFailure", "NOT the current call." ); - return; - } - - log.debug( "onCallTransferFailure", "Transfer failed." ); + log.info("Transfer failed."); + } + + private boolean isCurrentCallOrCallTransfer(Call call) { + return (call == this.call) || (call != callTransfer); + } + + private boolean isCurrentCall(Call call) { + return this.call == call; } } diff --git a/bbb-voice/src/main/java/org/red5/app/sip/SipUserAgentListener.java b/bbb-voice/src/main/java/org/red5/app/sip/SipUserAgentListener.java index 4c825aadc9..662b56caba 100644 --- a/bbb-voice/src/main/java/org/red5/app/sip/SipUserAgentListener.java +++ b/bbb-voice/src/main/java/org/red5/app/sip/SipUserAgentListener.java @@ -1,33 +1,12 @@ package org.red5.app.sip; -import org.zoolu.sip.address.NameAddress; - -/** Listener of SIPUserAgent */ public interface SipUserAgentListener { - /** When a new call is incoming */ - public void onUaCallIncoming( SipUserAgent ua, NameAddress callee, NameAddress caller ); - - /** When an incoming call is cancelled */ - public void onUaCallCancelled( SipUserAgent ua ); - - /** When an ougoing call is remotly ringing */ - public void onUaCallRinging( SipUserAgent ua ); - - /** When an ougoing call has been accepted */ - public void onUaCallAccepted( SipUserAgent ua ); - - /** When a call has been trasferred */ - public void onUaCallTrasferred( SipUserAgent ua ); - - /** When an ougoing call has been refused or timeout */ - public void onUaCallFailed( SipUserAgent ua ); - - - /** When a call has been locally or remotely closed */ - public void onUaCallClosed(SipUserAgent ua); - - - /** When a call has media connected */ - public void onUaCallConnected(SipUserAgent ua, String talkStreamName, String listenStreamName); - + public void onCallConnected(String talkStreamName, String listenStreamName); + public void onNewIncomingCall(String source, String sourceName, String destination, String destinationName); + public void onOutgoingCallRemoteRinging(); + public void onOutgoingCallAccepted(); + public void onCallTrasferred(); + public void onIncomingCallCancelled(); + public void onOutgoingCallFailed(); + public void onCallClosed(); }