/* * Copyright (C) 2005 Luca Veltri - University of Parma - Italy * * This source code is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This source code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this source code; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * Author(s): * Luca Veltri (luca.veltri@unipr.it) */ package net.sourceforge.gjtapi.raw.mjsip.ua; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.logging.Level; import java.util.logging.Logger; import javax.telephony.ConnectionEvent; import javax.telephony.Event; import javax.telephony.ProviderUnavailableException; import local.ua.RegisterAgent; import local.ua.RegisterAgentListener; import net.sourceforge.gjtapi.CallId; import net.sourceforge.gjtapi.raw.mjsip.MjSipCallId; import net.sourceforge.gjtapi.raw.mjsip.MjSipProvider; import org.zoolu.sip.address.NameAddress; import org.zoolu.sip.address.SipURL; import org.zoolu.sip.provider.SipProvider; import org.zoolu.sip.provider.SipStack; /** Simple command-line-based SIP user agent (UA). * It includes audio/video applications. * <p>It can use external audio/video tools as media applications. * Currently only RAT (Robust Audio Tool) and VIC are supported as external applications. */ public class UA implements UserAgentListener, RegisterAgentListener { /** Logger instance. */ private static final Logger LOGGER = Logger.getLogger(MjSipProvider.class.getName()); /** User Agent */ private final UserAgent ua; /** Register Agent */ private final RegisterAgent ra; /** UserAgentProfile */ private final UserAgentProfile userProfile; /** The record stream. */ private final InputStreamConverter convertedInStream; /** The play stream. */ private final OutputStreamConverter convertedOutStream; /** SIP Provder */ private final SipProvider sipProvider; /** MjSipUAProvider Provder */ private final MjSipProvider provider; /** Id from current call * It's assumed an agent can only handle one call at a time */ private CallId callID; /** Currently handled address. * It's assumed an agent can only handle one call at a time */ private String processingAddress; /*private float FrameRate8Khz = 8000.0F; private float FrameRate16Khz = 16000.0F; private AudioFormat linear8Khz = new AudioFormat(AudioFormat.Encoding. PCM_SIGNED, FrameRate8Khz, 16, 1, 2, FrameRate8Khz, false); private AudioFormat linear16Khz = new AudioFormat(AudioFormat.Encoding. PCM_SIGNED, FrameRate16Khz, 16, 1, 2, FrameRate8Khz, true); private AudioFormat ulawformat = new AudioFormat(AudioFormat.Encoding.ULAW, FrameRate8Khz, 8, 1, 1, FrameRate8Khz, false);*/ /** * Costructs a UA. * @param file name of the configuration file * @param provider related SIP provider. */ public UA(String file, MjSipProvider provider) { File check = new File(file); if (!check.exists()) { throw new ProviderUnavailableException( "Unable to open configuration file '" + check.getAbsoluteFile() + "'"); } this.provider = provider; SipStack.init(file); sipProvider = new SipProvider(file); userProfile = new UserAgentProfile(file); ua = new UserAgent(sipProvider, userProfile, this); //convertedOutStream = new OutputStreamConverter(ulawformat, linear8Khz); //convertedOutStream = new OutputStreamConverter(ulawformat, ulawformat); convertedOutStream = new OutputStreamConverter(); //convertedInStream = new InputStreamConverter(linear16Khz, ulawformat); //convertedInStream = new InputStreamConverter(ulawformat, ulawformat); convertedInStream = new InputStreamConverter(); ua.setRecvStream(convertedOutStream); ua.setSendStream(convertedInStream); ua.setAudio(true); //to indicate that we want to use the audio_line ra = new RegisterAgent(sipProvider, userProfile.from_url, userProfile.contact_url, userProfile.username, userProfile.realm, userProfile.passwd, this); run(); } /** Register with the registrar server. * @param expireTime expiration time in seconds */ public void register(int expireTime) { if (ra.isRegistering()) { ra.halt(); } ra.register(expireTime); } /** Periodically registers the contact address 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 (ra.isRegistering()) { ra.halt(); } ra.loopRegister(expireTime, renewTime, keepaliveTime); } /** Unregister with the registrar server */ public void unregister() { if (ra.isRegistering()) { ra.halt(); } ra.unregister(); } /** Unregister all contacts with the registrar server */ public void unregisterall() { if (ra.isRegistering()) { ra.halt(); } ra.unregisterall(); } /** Makes a new call */ public void call(String targetUrl) { ua.hangup(); LOGGER.info("UAC: CALLING " + targetUrl); if (!ua.user_profile.audio && !ua.user_profile.video) { LOGGER.info("ONLY SIGNALING, NO MEDIA"); } ua.call(targetUrl); processingAddress = targetUrl; } /** Receives incoming calls (auto accept) */ public void listen() { LOGGER.info("UAS: WAITING FOR INCOMING CALL"); if (!ua.user_profile.audio && !ua.user_profile.video) { LOGGER.info("ONLY SIGNALING, NO MEDIA"); } ua.listen(); } /** Starts the UA */ void run() { try { // Set the re-invite if (userProfile.re_invite_time > 0) { ua.reInvite(userProfile.contact_url, userProfile.re_invite_time); } // Set the transfer (REFER) if (userProfile.transfer_to != null && userProfile.transfer_time > 0) { ua.callTransfer(userProfile.transfer_to, userProfile.transfer_time); } if (userProfile.do_unregister_all) // ########## unregisters ALL contact URLs { LOGGER.info("UNREGISTER ALL contact URLs"); unregisterall(); } if (userProfile.do_unregister) // unregisters the contact URL { LOGGER.info("UNREGISTER the contact URL"); unregister(); } if (userProfile.do_register) // ########## registers the contact URL with the registrar server { LOGGER.info("REGISTRATION"); loopRegister(userProfile.expires, userProfile.expires / 2, userProfile.keepalive_time); } if (userProfile.call_to != null) { // UAC call(userProfile.call_to); ua.hangup(); } else { // UAS if (userProfile.accept_time >= 0) { LOGGER.info("UAS: AUTO ACCEPT MODE"); } listen(); } } catch (Exception e) { LOGGER.severe(e.getMessage()); } } // ******************* UserAgent callback functions ****************** /** When a new call is incoming */ public void onUaCallIncoming(UserAgent ua, NameAddress callee, NameAddress caller) { LOGGER.info("incoming call from " + caller.toString() + " to " + callee.toString()); callID = new MjSipCallId(); final SipURL address = callee.getAddress(); processingAddress = address.toString(); provider.terminalConnectionRinging(callID, address.toString(), address.toString(), ConnectionEvent.CAUSE_NORMAL); provider.connectionInProgress(callID, address.toString(), Event.CAUSE_NORMAL); provider.connectionAlerting(callID, address.toString(), ConnectionEvent.CAUSE_NORMAL); } /** When an outgoing call is remotely ringing */ public void onUaCallRinging(UserAgent ua) { provider.terminalConnectionCreated(callID, userProfile.contact_url, userProfile.contact_url, ConnectionEvent.CAUSE_NORMAL); provider.connectionInProgress(callID, userProfile.contact_url, Event.CAUSE_NORMAL); provider.connectionAlerting(callID, userProfile.contact_url, ConnectionEvent.CAUSE_NORMAL); } /** When an outgoing call has been accepted */ public void onUaCallAccepted(UserAgent ua) { provider.connectionConnected(callID, processingAddress, ConnectionEvent.CAUSE_NORMAL); provider.callActive(callID, Event.CAUSE_NORMAL); } /** When a call has been transferred */ public void onUaCallTrasferred(UserAgent ua) { } /** When an incoming call has been canceled */ public void onUaCallCancelled(UserAgent ua) { listen(); } /** When an outgoing call has been refused or timeout */ public void onUaCallFailed(UserAgent ua) { if (ua.user_profile.call_to == null) { listen(); } } /** When a call has been locally or remotely closed */ public void onUaCallClosed(UserAgent ua) { if (ua.user_profile.call_to == null) { listen(); } provider.connectionDisconnected(callID, userProfile.contact_url, Event.CAUSE_NORMAL); } // **************** RegisterAgent callback functions ***************** /** When a UA has been successfully (un)registered. */ public void onUaRegistrationSuccess(RegisterAgent ra, NameAddress target, NameAddress contact, String result) { LOGGER.info("Registration success: " + result); } /** When a UA failed on (un)registering. */ public void onUaRegistrationFailure(RegisterAgent ra, NameAddress target, NameAddress contact, String result) { LOGGER.info("Registration failure: " + result); } // **************************** GJTAPI Specific *************************** /** Returns the User Agent contact address */ public String getAddress() { return ua.user_profile.contact_url; } public void setCallId(CallId id) { callID = id; } public CallId getCallId() { return callID; } /** Accept incoming call */ public void accept() { ua.accept(); } /** Hangup call */ public void hangup() { ua.hangup(); } /** Close media Application */ public void closeMediaApplication() { stop(); ua.closeMediaApplication(); } public void play(InputStream src) { convertedInStream.setInputStream(src); convertedInStream.waitForEnd(); } public void record(OutputStream dest) { convertedOutStream.setOutputStream(dest); try { while (convertedOutStream.isOpen()) { Thread.sleep(10); } } catch (InterruptedException ex) { return; } } public void stopRecord() { try { if (convertedOutStream != null) { convertedOutStream.close(); try { while (convertedOutStream.isOpen()) { Thread.sleep(10); } } catch (InterruptedException ex) { return; } } } catch (IOException e) { if (LOGGER.isLoggable(Level.FINE)) { LOGGER.fine(e.getMessage()); } } } public void stopPlay() { try { if (convertedInStream != null) { convertedInStream.close(); convertedInStream.waitForEnd(); } } catch (IOException e) { LOGGER.warning(e.getMessage()); } } public void stop() { LOGGER.info("GJTAPI: Media stopped"); stopPlay(); stopRecord(); } }