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.RequestedAction; import jain.protocol.ip.mgcp.message.parms.RequestedEvent; import jain.protocol.ip.mgcp.message.parms.ReturnCode; import java.net.URI; import java.util.ArrayList; import java.util.LinkedList; 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.Parameters; import javax.media.mscontrol.Value; import javax.media.mscontrol.mediagroup.MediaGroup; import javax.media.mscontrol.mediagroup.Player; import javax.media.mscontrol.mediagroup.PlayerEvent; import javax.media.mscontrol.resource.RTC; import javax.media.mscontrol.resource.ResourceEvent; import org.apache.log4j.Logger; 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 PlayerImpl implements Player { private static Logger logger = Logger.getLogger(PlayerImpl.class); protected MediaGroupImpl mediaGroup = null; protected CopyOnWriteArrayList<MediaEventListener<PlayerEvent>> mediaEventListenerList = new CopyOnWriteArrayList<MediaEventListener<PlayerEvent>>(); protected MediaSessionImpl mediaSession = null; protected MgcpWrapper mgcpWrapper = null; private MediaConfigImpl config = null; protected volatile RequestIdentifier reqId = null; protected volatile PlayerState state = PlayerState.IDLE; private volatile LinkedList<Runnable> txList = new LinkedList<Runnable>(); private List<EventName> eveNames = new ArrayList<EventName>(); protected PlayerImpl(MediaGroupImpl mediaGroup, MgcpWrapper mgcpWrapper, MediaConfigImpl config) throws MsControlException { this.mediaGroup = mediaGroup; this.mediaSession = (MediaSessionImpl) mediaGroup.getMediaSession(); this.mgcpWrapper = mgcpWrapper; this.config = config; } private void updateState() { if (txList.size() == 0) { this.state = PlayerState.IDLE; } else { this.state = PlayerState.ACTIVE; } } private void executeNextTx() { Runnable nextTx = txList.poll(); if (nextTx != null) { this.state = PlayerState.ACTIVE; Provider.submit(nextTx); } } // Player methods public void play(URI[] uris, RTC[] arg1, Parameters params) throws MsControlException { for (URI uri : uris) { this.play(uri, arg1, params); } } public void play(URI uri, RTC[] arg1, Parameters params) throws MsControlException { if (MediaObjectState.JOINED.equals(this.mediaGroup.getState())) { if (this.state == PlayerState.ACTIVE) { Object obj = null; if (params != null && (obj = params.get(BEHAVIOUR_IF_BUSY)) != null) { Value action = (Value) obj; if (action == Player.QUEUE_IF_BUSY) { Runnable tx = new StartTx(this, uri); txList.add(tx); } else if (action == Player.STOP_IF_BUSY) { txList.clear(); Runnable tx = new StartTx(this, uri); txList.add(tx); // Stop the Player first Runnable tx1 = new StopTx(this); Provider.submit(tx1); } else if (action == Player.FAIL_IF_BUSY) { throw new MsControlException("Player is busy"); } else { logger .error("The Value " + action + " is not recognized for Parameter p_IfBusy. It has to be one of Player.v_Queue, Player.v_Stop or Player.v_Fail"); } } else { logger .warn("The Player is busy and no Parameter BEHAVIOUR_IF_BUSY passed to take necessary action"); } } else { Runnable tx = new StartTx(this, uri); Provider.submit(tx); } this.state = PlayerState.ACTIVE; } else { throw new MsControlException(this.mediaGroup.getURI() + " Container is not joined to any other container"); } } // Resource Methods public MediaGroup getContainer() { return this.mediaGroup; } public boolean stop() { txList.clear(); if (this.state == PlayerState.ACTIVE) { Runnable tx = new StopTx(this); Provider.submit(tx); return true; } return false; } // MediaEventNotifier methods public void addListener(MediaEventListener<PlayerEvent> listener) { this.mediaEventListenerList.add(listener); } public void removeListener(MediaEventListener<PlayerEvent> listener) { this.mediaEventListenerList.remove(listener); } public MediaSession getMediaSession() { return this.mediaGroup.getMediaSession(); } protected void update(PlayerEvent anEvent) { for (MediaEventListener<PlayerEvent> m : mediaEventListenerList) { m.onEvent(anEvent); } } private class StopTx implements Runnable, JainMgcpExtendedListener { private int tx = -1; private PlayerImpl player = null; StopTx(PlayerImpl player) { this.player = player; } // TODO : This will stop all the active Signals and Event Detection! We // just want Player to Stop 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.setTransactionHandle(this.tx); notificationRequest.setNotifiedEntity(mgcpWrapper.getDefaultNotifiedEntity()); mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { notificationRequest }); } catch (Exception e) { logger.error(e); mgcpWrapper.removeListener(this.tx); mgcpWrapper.removeListener(reqId); updateState(); PlayerEventImpl event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.UNKNOWN_ERROR, "Error " + e.getMessage()); update(event); executeNextTx(); } } 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); PlayerEventImpl event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.UNKNOWN_ERROR, "No response from MGW for RQNT"); update(event); executeNextTx(); } 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); updateState(); PlayerEventImpl event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.UNKNOWN_ERROR, "RQNT Failed. Look at logs " + respEvent.getReturnCode().getComment()); update(event); executeNextTx(); break; } } private void processReqNotificationResponse(NotificationRequestResponse responseEvent) { PlayerEvent 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); updateState(); event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, true, ResourceEvent.STOPPED, null); update(event); executeNextTx(); break; default: logger.error(" SOMETHING IS BROKEN = " + responseEvent); mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); mgcpWrapper.removeListener(reqId); updateState(); event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.UNKNOWN_ERROR, "RQNT Failed. Look at logs " + responseEvent.getReturnCode().getComment()); update(event); executeNextTx(); break; } } } private class StartTx implements Runnable, JainMgcpExtendedListener { private int tx = -1; private PlayerImpl player = null; private URI file = null; StartTx(PlayerImpl player, URI file) { this.player = player; this.file = file; } 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); ConnectionIdentifier connId = mediaGroup.thisConnId; EventName[] signalRequests = null; for (DefaultEventGeneratorFactory genfact : config.getPlayerGeneFactList()) { if (genfact.getEventName().compareTo("ann") == 0) { String filePath = file.toString(); eveNames.add(genfact.generateMgcpEvent(filePath, connId)); } else { eveNames.add(genfact.generateMgcpEvent(null, connId)); } } signalRequests = new EventName[eveNames.size()]; eveNames.toArray(signalRequests); eveNames.clear(); notificationRequest.setSignalRequests(signalRequests); // Request Event RequestedAction[] actions = new RequestedAction[] { RequestedAction.NotifyImmediately }; for (PlayerEventDetectorFactory detfact : config.getPlayerDetFactList()) { 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), actions); } eveNames.clear(); notificationRequest.setRequestedEvents(requestedEvents); notificationRequest.setTransactionHandle(this.tx); notificationRequest.setNotifiedEntity(mgcpWrapper.getDefaultNotifiedEntity()); mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { notificationRequest }); } catch (Exception e) { logger.error(e); mgcpWrapper.removeListener(this.tx); mgcpWrapper.removeListener(reqId); updateState(); PlayerEventImpl event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.UNKNOWN_ERROR, "Error while sending RQNt " + e.getMessage()); update(event); executeNextTx(); } } 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); updateState(); PlayerEventImpl event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.UNKNOWN_ERROR, "No response from MGW for RQNT"); update(event); executeNextTx(); } public void processMgcpCommandEvent(JainMgcpCommandEvent command) { logger.debug(" The NTFY received " + command.toString()); PlayerEventImpl event = null; Notify notify = (Notify) command; mgcpWrapper.removeListener(notify.getRequestIdentifier()); switch (command.getObjectIdentifier()) { case Constants.CMD_NOTIFY: EventName[] observedEvents = notify.getObservedEvents(); updateState(); for (EventName observedEvent : observedEvents) { for (PlayerEventDetectorFactory detfact : config.getPlayerDetFactList()) { if ((detfact.getPkgName().compareTo(observedEvent.getPackageName().toString()) == 0) && (detfact.getEventName().compareTo(observedEvent.getEventIdentifier().getName()) == 0)) { event = (PlayerEventImpl) detfact.generateMediaEvent(); event.setPlayer(this.player); event.setSuccessful(true); event.setQualifier(ResourceEvent.STANDARD_COMPLETION); update(event); executeNextTx(); } } } NotifyResponse response = new NotifyResponse(notify.getSource(), ReturnCode.Transaction_Executed_Normally); response.setTransactionHandle(notify.getTransactionHandle()); mgcpWrapper.sendMgcpEvents(new JainMgcpEvent[] { response }); break; default: logger.error("Expected NTFY cmd. Received " + command); updateState(); event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.UNKNOWN_ERROR, "Player failed on Server"); update(event); executeNextTx(); break; } } 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); updateState(); logger.warn(" This RESPONSE is unexpected " + respEvent); PlayerEventImpl event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.UNKNOWN_ERROR, "RQNT Failed. Look at logs " + respEvent.getReturnCode().getComment()); update(event); executeNextTx(); break; } } private void processReqNotificationResponse(NotificationRequestResponse responseEvent) { PlayerEvent 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); updateState(); event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.RESOURCE_UNAVAILABLE, "RQNT Failed. Look at logs " + responseEvent.getReturnCode().getComment()); update(event); executeNextTx(); break; default: logger.error(" SOMETHING IS BROKEN = " + responseEvent); mgcpWrapper.removeListener(responseEvent.getTransactionHandle()); mgcpWrapper.removeListener(reqId); updateState(); event = new PlayerEventImpl(this.player, PlayerEvent.PLAY_COMPLETED, false, MediaErr.UNKNOWN_ERROR, "RQNT Failed. Look at logs " + responseEvent.getReturnCode().getComment()); update(event); executeNextTx(); break; } } } }