package org.mobicents.javax.media.mscontrol.mediagroup; 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.ReturnCode; import jain.protocol.ip.mgcp.pkg.MgcpEvent; import java.net.URI; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; import javax.media.mscontrol.MediaErr; import javax.media.mscontrol.MediaEvent; import javax.media.mscontrol.MediaEventListener; import javax.media.mscontrol.MediaSession; import javax.media.mscontrol.MsControlException; import javax.media.mscontrol.Parameters; import javax.media.mscontrol.mediagroup.MediaGroup; import javax.media.mscontrol.mediagroup.Recorder; import javax.media.mscontrol.mediagroup.RecorderEvent; import javax.media.mscontrol.resource.RTC; import org.apache.log4j.Logger; import org.mobicents.jain.protocol.ip.mgcp.pkg.AUMgcpEvent; import org.mobicents.javax.media.mscontrol.DefaultEventGeneratorFactory; import org.mobicents.javax.media.mscontrol.MediaConfigImpl; import org.mobicents.javax.media.mscontrol.MediaObjectState; import org.mobicents.javax.media.mscontrol.MediaSessionImpl; import org.mobicents.jsr309.mgcp.MgcpWrapper; import org.mobicents.jsr309.mgcp.Provider; import org.mobicents.mgcp.stack.JainMgcpExtendedListener; /** * * @author amit bhayani * */ public class RecorderImpl implements Recorder { private static Logger logger = Logger.getLogger(RecorderImpl.class); protected MediaGroupImpl mediaGroup = null; protected CopyOnWriteArrayList<MediaEventListener<? extends MediaEvent<?>>> mediaEventListenerList = new CopyOnWriteArrayList<MediaEventListener<? extends MediaEvent<?>>>(); protected MediaSessionImpl mediaSession = null; protected MgcpWrapper mgcpWrapper = null; protected RequestIdentifier reqId = null; protected RecorderState state = RecorderState.IDLE; private MediaConfigImpl config = null; private List<EventName> eveNames = new ArrayList<EventName>(); public RecorderImpl(MediaGroupImpl mediaGroup, MgcpWrapper mgcpWrapper, MediaConfigImpl config) { this.mediaGroup = mediaGroup; this.mediaSession = (MediaSessionImpl) mediaGroup.getMediaSession(); this.mgcpWrapper = mgcpWrapper; this.config = config; } public void record(URI streamID, RTC[] rtc, Parameters optargs) throws MsControlException { if (MediaObjectState.JOINED.equals(this.mediaGroup.getState())) { if (this.state == RecorderState.ACTIVE) { throw new MsControlException(this.mediaGroup.getURI() + " Recorder already ACTIVE"); } else { Runnable tx = new StartTx(this, streamID); Provider.submit(tx); } this.state = RecorderState.ACTIVE; } 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 == RecorderState.ACTIVE) { Runnable tx = new StopTx(this); Provider.submit(tx); return true; } return false; } public void addListener(MediaEventListener<RecorderEvent> listener) { this.mediaEventListenerList.add(listener); } public MediaSession getMediaSession() { return this.mediaSession; } public void removeListener(MediaEventListener<RecorderEvent> listener) { this.mediaEventListenerList.remove(listener); } protected void update(RecorderEvent anEvent) { for (MediaEventListener m : mediaEventListenerList) { m.onEvent(anEvent); } } private class StopTx implements Runnable, JainMgcpExtendedListener { private int tx = -1; private RecorderImpl recorder = null; StopTx(RecorderImpl recorder) { this.recorder = recorder; } 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); RecorderEventImpl event = new RecorderEventImpl(this.recorder, RecorderEvent.COMPLETED, false, MediaErr.UNKNOWN_ERROR, "Error while sending RQNt " + e.getMessage()); update(event); } } public void transactionEnded(int arg0) { // TODO Auto-generated method stub } public void transactionRxTimedOut(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); RecorderEventImpl event = new RecorderEventImpl(this.recorder, RecorderEvent.COMPLETED, false, MediaErr.UNKNOWN_ERROR, "No response from MGW for RQNT"); update(event); } public void transactionTxTimedOut(JainMgcpCommandEvent arg0) { // TODO Auto-generated method stub } public void processMgcpCommandEvent(JainMgcpCommandEvent arg0) { // 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); logger.warn(" This RESPONSE is unexpected " + respEvent); RecorderEventImpl event = new RecorderEventImpl(this.recorder, RecorderEvent.COMPLETED, false, MediaErr.UNKNOWN_ERROR, "RQNT Failed. Look at logs " + respEvent.getReturnCode().getComment()); update(event); break; } } private void processReqNotificationResponse(NotificationRequestResponse responseEvent) { RecorderEventImpl 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()); mgcpWrapper.removeListener(reqId); state = RecorderState.IDLE; event = new RecorderEventImpl(this.recorder, RecorderEvent.COMPLETED, true); update(event); break; default: logger.error(" SOMETHING IS BROKEN = " + responseEvent); mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); mgcpWrapper.removeListener(reqId); event = new RecorderEventImpl(this.recorder, RecorderEvent.COMPLETED, false, MediaErr.UNKNOWN_ERROR, "RQNT Failed. Look at logs " + responseEvent.getReturnCode().getComment()); update(event); break; } } } private class StartTx implements Runnable, JainMgcpExtendedListener { private int tx = -1; private Recorder recorder = null; private String file = null; StartTx(Recorder recorder, URI uri) { this.recorder = recorder; this.file = uri.toString(); } 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; EventName[] signalRequests = null; for (DefaultEventGeneratorFactory genfact : config.getRecorderGeneFactList()) { if (genfact.getEventName().compareTo(AUMgcpEvent.aupr.getName()) == 0) { eveNames.add(genfact.generateMgcpEvent(this.file, connId)); } else { eveNames.add(genfact.generateMgcpEvent(null, connId)); } } signalRequests = new EventName[eveNames.size()]; eveNames.toArray(signalRequests); eveNames.clear(); notificationRequest.setSignalRequests(signalRequests); // TODO : These are not supported yet on MMS // RequestedAction[] actions = new RequestedAction[] { RequestedAction.NotifyImmediately }; // RequestedEvent[] requestedEvents = { // new RequestedEvent(new EventName(AUPackage.AU, AUMgcpEvent.auoc, connId), actions), // new RequestedEvent(new EventName(AUPackage.AU, AUMgcpEvent.auof, connId), actions) }; // notificationRequest.setRequestedEvents(requestedEvents); notificationRequest.setTransactionHandle(this.tx); mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { notificationRequest }); } catch (Exception e) { logger.error(e); mgcpWrapper.removeListener(this.tx); mgcpWrapper.removeListener(reqId); RecorderEventImpl event = new RecorderEventImpl(this.recorder, RecorderEvent.STARTED, false, MediaErr.UNKNOWN_ERROR, "Error while sending RQNt " + e.getMessage()); update(event); } } 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); RecorderEventImpl event = new RecorderEventImpl(this.recorder, RecorderEvent.STARTED, false, MediaErr.UNKNOWN_ERROR, "No response from MGW for RQNT"); update(event); } public void processMgcpCommandEvent(JainMgcpCommandEvent command) { logger.debug(" The NTFY received " + command.toString()); Notify notify = (Notify) command; EventName[] observedEvents = notify.getObservedEvents(); RecorderEvent event = null; //TODO : The MediaEvent should come from Factory for (EventName observedEvent : observedEvents) { switch (observedEvent.getEventIdentifier().intValue()) { case AUMgcpEvent.OPERATION_COMPLETE: mgcpWrapper.removeListener(notify.getRequestIdentifier()); state = RecorderState.IDLE; MgcpEvent mgcpEvent = observedEvent.getEventIdentifier(); String params = mgcpEvent.getParms(); event = new RecorderEventImpl(this.recorder, RecorderEvent.COMPLETED, true, RecorderEvent.DURATION_EXCEEDED, null, getInterval(params)); update(event); break; case AUMgcpEvent.OPERATION_FAIL: mgcpWrapper.removeListener(notify.getRequestIdentifier()); event = new RecorderEventImpl(this.recorder, RecorderEvent.COMPLETED, false, MediaErr.UNKNOWN_ERROR, "Recorder failed on server "); update(event); break; } } NotifyResponse response = new NotifyResponse(notify.getSource(), ReturnCode.Transaction_Executed_Normally); response.setTransactionHandle(notify.getTransactionHandle()); mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { response }); } private int getInterval(String params) { // Let us remove all SP and HTAB // params.replaceAll("[\\x20]", ""); // params.replaceAll("[\\x20]", ""); int inter = -1; if (params != null) { char[] cArr = params.toCharArray(); int start = 0; int end = 0; for (int i = 0; i < cArr.length - 1; i++) { if (cArr[i] == 'i' && cArr[i + 1] == 'v') { start = i; break; } }// end of for boolean decoded = true; int count = start + 3; String interval = ""; while (decoded && (count < cArr.length)) { char ch = cArr[count]; if (ch >= '0' && ch <= '9') { count++; interval = interval + ch; } else { decoded = false; } }// while try { inter = Integer.parseInt(interval); } catch (NumberFormatException e) { logger.error("Parsing of interval failed ", e); } } return inter; } 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); RecorderEventImpl event = new RecorderEventImpl(this.recorder, RecorderEvent.STARTED, false, MediaErr.UNKNOWN_ERROR, "RQNT Failed. Look at logs " + respEvent.getReturnCode().getComment()); update(event); break; } } private void processReqNotificationResponse(NotificationRequestResponse responseEvent) { RecorderEvent 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 RecorderEventImpl(this.recorder, RecorderEvent.STARTED, 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 RecorderEventImpl(this.recorder, RecorderEvent.STARTED, false, MediaErr.UNKNOWN_ERROR, "RQNT Failed. Look at logs " + responseEvent.getReturnCode().getComment()); update(event); break; } } } }