package org.mobicents.javax.media.mscontrol.mediagroup.signals;
import jain.protocol.ip.mgcp.JainMgcpCommandEvent;
import jain.protocol.ip.mgcp.JainMgcpEvent;
import jain.protocol.ip.mgcp.JainMgcpResponseEvent;
import jain.protocol.ip.mgcp.message.Constants;
import jain.protocol.ip.mgcp.message.NotificationRequest;
import jain.protocol.ip.mgcp.message.NotificationRequestResponse;
import jain.protocol.ip.mgcp.message.Notify;
import jain.protocol.ip.mgcp.message.NotifyResponse;
import jain.protocol.ip.mgcp.message.parms.ConnectionIdentifier;
import jain.protocol.ip.mgcp.message.parms.EndpointIdentifier;
import jain.protocol.ip.mgcp.message.parms.EventName;
import jain.protocol.ip.mgcp.message.parms.RequestIdentifier;
import jain.protocol.ip.mgcp.message.parms.RequestedAction;
import jain.protocol.ip.mgcp.message.parms.RequestedEvent;
import jain.protocol.ip.mgcp.message.parms.ReturnCode;
import jain.protocol.ip.mgcp.pkg.MgcpEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import javax.media.mscontrol.MediaErr;
import javax.media.mscontrol.MediaEventListener;
import javax.media.mscontrol.MediaSession;
import javax.media.mscontrol.MsControlException;
import javax.media.mscontrol.Parameter;
import javax.media.mscontrol.Parameters;
import javax.media.mscontrol.mediagroup.MediaGroup;
import javax.media.mscontrol.mediagroup.signals.SignalDetector;
import javax.media.mscontrol.mediagroup.signals.SignalDetectorEvent;
import javax.media.mscontrol.resource.RTC;
import javax.media.mscontrol.resource.enums.ParameterEnum;
import org.apache.log4j.Logger;
import org.mobicents.javax.media.mscontrol.MediaConfigImpl;
import org.mobicents.javax.media.mscontrol.MediaObjectState;
import org.mobicents.javax.media.mscontrol.MediaSessionImpl;
import org.mobicents.javax.media.mscontrol.mediagroup.MediaGroupImpl;
import org.mobicents.jsr309.mgcp.MgcpWrapper;
import org.mobicents.jsr309.mgcp.Provider;
import org.mobicents.mgcp.stack.JainMgcpExtendedListener;
/**
*
* @author amit bhayani
*
*/
public class SignalDetectorImpl implements SignalDetector {
private static Logger logger = Logger.getLogger(SignalDetectorImpl.class);
protected CopyOnWriteArrayList<MediaEventListener<SignalDetectorEvent>> mediaEventListenerList = new CopyOnWriteArrayList<MediaEventListener<SignalDetectorEvent>>();
protected MediaGroupImpl mediaGroup = null;
protected MediaSessionImpl mediaSession = null;
protected MgcpWrapper mgcpWrapper = null;
protected volatile RequestIdentifier reqId = null;
private MediaConfigImpl config = null;
private List<EventName> eveNames = new ArrayList<EventName>();
// TODO : Not really caring about State as of now
protected volatile SignalDetectorState state = SignalDetectorState.IDLE;
// TODO : Buffer needs to be implemented
private List<String> buffer = null;
public SignalDetectorImpl(MediaGroupImpl mediaGroup, MgcpWrapper mgcpWrapper, MediaConfigImpl config) {
this.mediaGroup = mediaGroup;
this.mgcpWrapper = mgcpWrapper;
this.config = config;
this.mediaSession = (MediaSessionImpl) mediaGroup.getMediaSession();
this.buffer = new ArrayList<String>();
}
public void flushBuffer() throws MsControlException {
this.buffer.clear();
}
public void receiveSignals(int numSignals, Parameter[] patterns, RTC[] rtc, Parameters optargs)
throws MsControlException {
if (MediaObjectState.JOINED.equals(this.mediaGroup.getState())) {
Runnable tx = new StartTx(this, patterns, optargs);
Provider.submit(tx);
this.state = SignalDetectorState.DETECTING;
} else {
throw new MsControlException(this.mediaGroup.getURI() + " Container is not joined to any other container");
}
}
public MediaGroup getContainer() {
return this.mediaGroup;
}
public boolean stop() {
if (this.state == SignalDetectorState.DETECTING) {
Runnable tx = new StopTx(this);
Provider.submit(tx);
return true;
}
return false;
}
public void addListener(MediaEventListener<SignalDetectorEvent> listener) {
this.mediaEventListenerList.add(listener);
}
public MediaSession getMediaSession() {
return this.mediaSession;
}
public void removeListener(MediaEventListener<SignalDetectorEvent> listener) {
this.mediaEventListenerList.remove(listener);
}
protected void update(SignalDetectorEvent anEvent) {
for (MediaEventListener<SignalDetectorEvent> m : mediaEventListenerList) {
m.onEvent(anEvent);
}
}
private class StopTx implements Runnable, JainMgcpExtendedListener {
private int tx = -1;
private SignalDetectorImpl detector = null;
StopTx(SignalDetectorImpl detector) {
this.detector = detector;
}
public void run() {
try {
this.tx = mgcpWrapper.getUniqueTransactionHandler();
mgcpWrapper.addListnere(this.tx, this);
mgcpWrapper.addListnere(reqId, this);
EndpointIdentifier endpointID = new EndpointIdentifier(mediaGroup.getEndpoint(), mgcpWrapper
.getPeerIp()
+ ":" + mgcpWrapper.getPeerPort());
NotificationRequest notificationRequest = new NotificationRequest(this, endpointID, reqId);
notificationRequest.setNotifiedEntity(mgcpWrapper.getDefaultNotifiedEntity());
notificationRequest.setTransactionHandle(this.tx);
mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { notificationRequest });
} catch (Exception e) {
logger.error(e);
}
}
public void transactionEnded(int arg0) {
// TODO Auto-generated method stub
}
public void transactionRxTimedOut(JainMgcpCommandEvent cmdEvent) {
}
public void transactionTxTimedOut(JainMgcpCommandEvent cmdEvent) {
logger.error("No response from MGW. Tx timed out for RQNT Tx " + this.tx + " For Command sent "
+ cmdEvent.toString());
mgcpWrapper.removeListener(cmdEvent.getTransactionHandle());
mgcpWrapper.removeListener(reqId);
state = SignalDetectorState.IDLE;
}
public void processMgcpCommandEvent(JainMgcpCommandEvent jainmgcpcommandevent) {
// TODO Auto-generated method stub
}
public void processMgcpResponseEvent(JainMgcpResponseEvent respEvent) {
switch (respEvent.getObjectIdentifier()) {
case Constants.RESP_NOTIFICATION_REQUEST:
processReqNotificationResponse((NotificationRequestResponse) respEvent);
break;
default:
mgcpWrapper.removeListener(respEvent.getTransactionHandle());
mgcpWrapper.removeListener(reqId);
state = SignalDetectorState.IDLE;
logger.warn(" This RESPONSE is unexpected " + respEvent);
break;
}
}
private void processReqNotificationResponse(NotificationRequestResponse responseEvent) {
ReturnCode returnCode = responseEvent.getReturnCode();
switch (returnCode.getValue()) {
case ReturnCode.TRANSACTION_BEING_EXECUTED:
// do nothing
if (logger.isDebugEnabled()) {
logger.debug("Transaction " + this.tx + "is being executed. Response received = " + responseEvent);
}
break;
case ReturnCode.TRANSACTION_EXECUTED_NORMALLY:
mgcpWrapper.removeListener(responseEvent.getTransactionHandle());
mgcpWrapper.removeListener(reqId);
state = SignalDetectorState.IDLE;
break;
default:
logger.error(" SOMETHING IS BROKEN = " + responseEvent);
mgcpWrapper.removeListener(responseEvent.getTransactionHandle());
mgcpWrapper.removeListener(reqId);
state = SignalDetectorState.IDLE;
break;
}
}
}
private class StartTx implements Runnable, JainMgcpExtendedListener {
/*
* NOTE : Assuming that reg-ex is similar to given in MGCP RFC
*/
private int tx = -1;
private SignalDetectorImpl detector = null;
private Parameter[] patterns = null;
private Parameters optargs = null;
String[] regExp = null;
String digitMap = null;
String digitDetected = "";
StartTx(SignalDetectorImpl detector, Parameter[] patterns, Parameters optargs) {
this.detector = detector;
this.patterns = patterns;
this.optargs = optargs;
boolean first = true;
if (optargs != null) {
regExp = new String[31];
for (Parameter p : optargs.keySet()) {
ParameterEnum pE = (ParameterEnum) p;
String regExtmp = (String) optargs.get(p);
// NOTE : Assuming that reg-ex is similar to given in MGCP
// RFC
regExtmp = regExtmp.replaceAll("x", "\\\\d");
regExtmp = regExtmp.replaceAll("\\*", "\\\\*");
regExtmp = regExtmp.replaceAll("\\.", "{0,}");
switch (pE) {
case SD_PATTERN_0:
regExp[0] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_0 = " + regExtmp);
}
break;
case SD_PATTERN_1:
regExp[1] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_1 = " + regExtmp);
}
break;
case SD_PATTERN_2:
regExp[2] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_2 = " + regExtmp);
}
break;
case SD_PATTERN_3:
regExp[3] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_3 = " + regExtmp);
}
break;
case SD_PATTERN_4:
regExp[4] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_4 = " + regExtmp);
}
break;
case SD_PATTERN_5:
regExp[5] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_5 = " + regExtmp);
}
break;
case SD_PATTERN_6:
regExp[6] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_6 = " + regExtmp);
}
break;
case SD_PATTERN_7:
regExp[7] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_7 = " + regExtmp);
}
break;
case SD_PATTERN_8:
regExp[8] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_8 = " + regExtmp);
}
break;
case SD_PATTERN_9:
regExp[9] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_9 = " + regExtmp);
}
break;
case SD_PATTERN_10:
regExp[10] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_10 = " + regExtmp);
}
break;
case SD_PATTERN_11:
regExp[11] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_11 = " + regExtmp);
}
break;
case SD_PATTERN_12:
regExp[12] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_12 = " + regExtmp);
}
break;
case SD_PATTERN_13:
regExp[13] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_13 = " + regExtmp);
}
break;
case SD_PATTERN_14:
regExp[14] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_14 = " + regExtmp);
}
break;
case SD_PATTERN_15:
regExp[15] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_15 = " + regExtmp);
}
break;
case SD_PATTERN_16:
regExp[16] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_16 = " + regExtmp);
}
break;
case SD_PATTERN_17:
regExp[17] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_17 = " + regExtmp);
}
break;
case SD_PATTERN_18:
regExp[18] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_18 = " + regExtmp);
}
break;
case SD_PATTERN_19:
regExp[19] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_19 = " + regExtmp);
}
break;
case SD_PATTERN_20:
regExp[20] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_20 = " + regExtmp);
}
break;
case SD_PATTERN_21:
regExp[21] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_21 = " + regExtmp);
}
break;
case SD_PATTERN_22:
regExp[22] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_22 = " + regExtmp);
}
break;
case SD_PATTERN_23:
regExp[23] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_23 = " + regExtmp);
}
break;
case SD_PATTERN_24:
regExp[24] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_24 = " + regExtmp);
}
break;
case SD_PATTERN_25:
regExp[25] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_25 = " + regExtmp);
}
break;
case SD_PATTERN_26:
regExp[26] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_26 = " + regExtmp);
}
break;
case SD_PATTERN_27:
regExp[27] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_27 = " + regExtmp);
}
break;
case SD_PATTERN_28:
regExp[28] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_28 = " + regExtmp);
}
break;
case SD_PATTERN_29:
regExp[29] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_29 = " + regExtmp);
}
break;
case SD_PATTERN_30:
regExp[30] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_30 = " + regExtmp);
}
break;
case SD_PATTERN_31:
regExp[31] = regExtmp;
if (logger.isDebugEnabled()) {
logger.debug("The Reg Exp for SD_PATTERN_31 = " + regExtmp);
}
break;
}
if (first) {
first = false;
digitMap = regExtmp;
} else {
digitMap = "|" + regExtmp;
}
}
if (logger.isDebugEnabled()) {
logger.debug("DigitMap formed is " + digitMap);
}
}
if (digitMap == null) {
digitMap = "[0-9, A,B,C,D,*,#]";
}
}
public void run() {
this.tx = mgcpWrapper.getUniqueTransactionHandler();
try {
mgcpWrapper.addListnere(this.tx, this);
EndpointIdentifier endpointID = new EndpointIdentifier(mediaGroup.getEndpoint(), mgcpWrapper
.getPeerIp()
+ ":" + mgcpWrapper.getPeerPort());
reqId = mgcpWrapper.getUniqueRequestIdentifier();
mgcpWrapper.addListnere(reqId, this);
NotificationRequest notificationRequest = new NotificationRequest(this, endpointID, reqId);
notificationRequest.setNotifiedEntity(mgcpWrapper.getDefaultNotifiedEntity());
ConnectionIdentifier connId = mediaGroup.thisConnId;
RequestedAction[] dtmfActions = new RequestedAction[] { RequestedAction.NotifyImmediately };
// notificationRequest.setDigitMap(new DigitMap(digitMap));
for (SignalDetectorEventDetectorFactory detfact : config.getSigDeteEveDetFactList()) {
eveNames.add(detfact.generateMgcpEvent(null, connId));
}
RequestedEvent[] requestedEvents = new RequestedEvent[eveNames.size()];
for (int i = 0; i < requestedEvents.length; i++) {
requestedEvents[i] = new RequestedEvent(eveNames.get(i), dtmfActions);
}
eveNames.clear();
notificationRequest.setRequestedEvents(requestedEvents);
notificationRequest.setTransactionHandle(this.tx);
mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { notificationRequest });
} catch (Exception e) {
logger.error(e);
state = SignalDetectorState.IDLE;
}
}
public void transactionEnded(int arg0) {
if (logger.isDebugEnabled()) {
logger.debug("Successfully completed Tx = " + arg0);
}
}
public void transactionRxTimedOut(JainMgcpCommandEvent arg0) {
if (logger.isDebugEnabled()) {
logger.debug("Couldn't send the Tx = " + arg0);
}
}
public void transactionTxTimedOut(JainMgcpCommandEvent cmdEvent) {
logger.error("No response from MGW. Tx timed out for RQNT Tx " + this.tx + " For Command sent "
+ cmdEvent.toString());
mgcpWrapper.removeListener(cmdEvent.getTransactionHandle());
mgcpWrapper.removeListener(reqId);
SignalDetectorEventImpl event = new SignalDetectorEventImpl(this.detector,
SignalDetectorEvent.SIGNAL_DETECTED, false, MediaErr.UNKNOWN_ERROR, "No response from MGW for RQNT");
update(event);
}
public void processMgcpCommandEvent(JainMgcpCommandEvent command) {
if (logger.isDebugEnabled()) {
logger.debug(" The NTFY received " + command.toString());
}
Notify notify = (Notify) command;
NotifyResponse response = new NotifyResponse(notify.getSource(), ReturnCode.Transaction_Executed_Normally);
response.setTransactionHandle(notify.getTransactionHandle());
mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { response });
// TODO : For now we de-register for every NTFY command
mgcpWrapper.removeListener(notify.getRequestIdentifier());
EventName[] observedEvents = notify.getObservedEvents();
SignalDetectorEventImpl event = null;
for (EventName observedEvent : observedEvents) {
for (SignalDetectorEventDetectorFactory detfact : config.getSigDeteEveDetFactList()) {
if ((detfact.getPkgName().compareTo(observedEvent.getPackageName().toString()) == 0)
&& (detfact.getEventName().compareTo(observedEvent.getEventIdentifier().getName()) == 0)) {
switch (observedEvent.getEventIdentifier().intValue()) {
case MgcpEvent.DTMF_0:
digitDetected = digitDetected + 0;
break;
case MgcpEvent.DTMF_1:
digitDetected = digitDetected + 1;
break;
case MgcpEvent.DTMF_2:
digitDetected = digitDetected + 2;
break;
case MgcpEvent.DTMF_3:
digitDetected = digitDetected + 3;
break;
case MgcpEvent.DTMF_4:
digitDetected = digitDetected + 4;
break;
case MgcpEvent.DTMF_5:
digitDetected = digitDetected + 5;
break;
case MgcpEvent.DTMF_6:
digitDetected = digitDetected + 6;
break;
case MgcpEvent.DTMF_7:
digitDetected = digitDetected + 7;
break;
case MgcpEvent.DTMF_8:
digitDetected = digitDetected + 8;
break;
case MgcpEvent.DTMF_9:
digitDetected = digitDetected + 9;
break;
case MgcpEvent.DTMF_A:
digitDetected = digitDetected + "A";
break;
case MgcpEvent.DTMF_B:
digitDetected = digitDetected + "B";
break;
case MgcpEvent.DTMF_C:
digitDetected = digitDetected + "C";
break;
case MgcpEvent.DTMF_D:
digitDetected = digitDetected + "D";
break;
case MgcpEvent.DTMF_HASH:
digitDetected = digitDetected + "#";
break;
case MgcpEvent.DTMF_STAR:
digitDetected = digitDetected + "*";
break;
default:
// TODO : ObservedEvent could be not DTMF. Need to take care
// latter
logger.error("Detected unexpected MGCP Event "
+ observedEvent.getEventIdentifier().toString());
break;
}
event = (SignalDetectorEventImpl) detfact.generateMediaEvent();
event.setDetector(detector);
event.setSuccessful(true);
event.setSignal(digitDetected);
update(event);
}
}
}
// TODO : Need to implement the patterns
// int count = -1;
// for (String s : regExp) {
// count++;
// if (s!=null && digitDetected.matches(s)) {
// break;
// }
// }
//
// if (count > -1) {
// event = new SignalDetectorEventImpl(this.detector,
// SignalDetector.ev_Pattern[count], digitDetected,
// count, SignalDetector.q_Pattern[count], null);
// } else {
// }
}
public void processMgcpResponseEvent(JainMgcpResponseEvent respEvent) {
switch (respEvent.getObjectIdentifier()) {
case Constants.RESP_NOTIFICATION_REQUEST:
processReqNotificationResponse((NotificationRequestResponse) respEvent);
break;
default:
mgcpWrapper.removeListener(respEvent.getTransactionHandle());
mgcpWrapper.removeListener(reqId);
logger.warn(" This RESPONSE is unexpected " + respEvent);
SignalDetectorEventImpl event = new SignalDetectorEventImpl(this.detector,
SignalDetectorEvent.SIGNAL_DETECTED, false, MediaErr.UNKNOWN_ERROR,
"RQNT Failed. Look at logs " + respEvent.getReturnCode().getComment());
update(event);
break;
}
}
private void processReqNotificationResponse(NotificationRequestResponse responseEvent) {
SignalDetectorEvent event = null;
ReturnCode returnCode = responseEvent.getReturnCode();
switch (returnCode.getValue()) {
case ReturnCode.TRANSACTION_BEING_EXECUTED:
// do nothing
if (logger.isDebugEnabled()) {
logger.debug("Transaction " + this.tx + "is being executed. Response received = " + responseEvent);
}
break;
case ReturnCode.TRANSACTION_EXECUTED_NORMALLY:
mgcpWrapper.removeListener(responseEvent.getTransactionHandle());
break;
case ReturnCode.ENDPOINT_INSUFFICIENT_RESOURCES:
mgcpWrapper.removeListener(responseEvent.getTransactionHandle());
mgcpWrapper.removeListener(reqId);
event = new SignalDetectorEventImpl(this.detector, SignalDetectorEvent.SIGNAL_DETECTED, false,
MediaErr.RESOURCE_UNAVAILABLE, "RQNT Failed. Look at logs "
+ responseEvent.getReturnCode().getComment());
update(event);
break;
default:
logger.error(" SOMETHING IS BROKEN = " + responseEvent);
mgcpWrapper.removeListener(responseEvent.getTransactionHandle());
mgcpWrapper.removeListener(reqId);
event = new SignalDetectorEventImpl(this.detector, SignalDetectorEvent.SIGNAL_DETECTED, false,
MediaErr.UNKNOWN_ERROR, "RQNT Failed. Look at logs "
+ responseEvent.getReturnCode().getComment());
update(event);
break;
}
}
}
}