/servers/media/jsr-309/src/main/java/org/mobicents/javax/media/mscontrol/mediagroup/PlayerImpl.java
Java | 594 lines | 420 code | 121 blank | 53 comment | 49 complexity | bc457084d5cc1ca538fd8746425a59c8 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
- package org.mobicents.javax.media.mscontrol.mediagroup;
- import jain.protocol.ip.mgcp.JainMgcpCommandEvent;
- import jain.protocol.ip.mgcp.JainMgcpListener;
- 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.Notify;
- 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.net.URI;
- import java.util.ArrayList;
- import java.util.concurrent.ConcurrentLinkedQueue;
- import java.util.concurrent.CopyOnWriteArrayList;
- import java.util.concurrent.locks.Condition;
- import java.util.concurrent.locks.ReentrantLock;
- 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.Qualifier;
- import javax.media.mscontrol.UnsupportedException;
- 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 org.mobicents.fsm.FSM;
- import org.mobicents.fsm.Logger;
- import org.mobicents.fsm.State;
- import org.mobicents.fsm.StateEventHandler;
- import org.mobicents.fsm.TransitionHandler;
- import org.mobicents.fsm.UnknownTransitionException;
- import org.mobicents.jsr309.mgcp.PackageAU;
- /**
- * b
- * @author amit bhayani
- *
- */
- public class PlayerImpl implements Player, JainMgcpListener, Logger {
- public final static String STATE_NULL = "NULL";
- public final static String STATE_IDLE = "IDLE";
- public final static String STATE_ACTIVE = "ACTIVE";
- public final static String STATE_ACTIVATING = "ACTIVATING";
- public final static String STATE_PAUSED = "PAUSED";
- public final static String STATE_INVALID = "INVALID";
-
- public final static String SIGNAL_CREATE = "CREATE";
- public final static String SIGNAL_PLAY = "PLAY";
- public final static String SIGNAL_STARTED = "STARTED";
- public final static String SIGNAL_FAILED = "FAILED";
- public final static String SIGNAL_START_PAUSED = "START_PAUSED";
- public final static String SIGNAL_STOP = "STOP";
- public final static String SIGNAL_PAUSE = "PAUSE";
- public final static String SIGNAL_RESUME = "RESUME";
- public final static String SIGNAL_PLAY_COMPLETED = "PLAY_COMPLETED";
- public final static String SIGNAL_RELEASE = "RELEASE";
-
- protected MediaGroupImpl parent = null;
- private FSM fsm;
-
- protected CopyOnWriteArrayList<MediaEventListener<PlayerEvent>> listeners = new CopyOnWriteArrayList<MediaEventListener<PlayerEvent>>();
-
- protected String uri;
- private String params;
- private String returnCode;
- private Qualifier qualifier;
-
- private ReentrantLock lock = new ReentrantLock();
- private Condition started = lock.newCondition();
- private long startTime;
- private long timeError = 100;
- private long stopTime;
- private long reqDuration;
-
- private ConcurrentLinkedQueue<PlayTask> playList = new ConcurrentLinkedQueue();
-
- //-------------------------------------------------------------------------------------------/
-
- protected PlayerImpl(MediaGroupImpl parent) throws MsControlException {
- this.parent = parent;
- this.initFSM();
- }
- private void initFSM() {
- fsm = new FSM(parent.getMediaSession().getDriver().getScheduler());
- fsm.setLogger(this);
-
- fsm.createState(STATE_NULL);
- fsm.createState(STATE_IDLE);
- fsm.createState(STATE_ACTIVATING);
- fsm.createState(STATE_ACTIVE).setOnEnter(new OnStart());
- fsm.createState(STATE_PAUSED);
- fsm.createState(STATE_INVALID);
-
- fsm.setStart(STATE_NULL);
- fsm.setEnd(STATE_INVALID);
-
- //state NULL
- fsm.createTransition(SIGNAL_CREATE, STATE_NULL, STATE_IDLE);
- fsm.createTransition(SIGNAL_RELEASE, STATE_NULL, STATE_INVALID);
-
- //state IDLE
- //sending request to server and waiting response
- fsm.createTransition(SIGNAL_PLAY, STATE_IDLE, STATE_ACTIVATING).setHandler(new PlayRequest());
- //player started in suspended mode, nothing is sent to server in this case
- fsm.createTransition(SIGNAL_START_PAUSED, STATE_IDLE, STATE_PAUSED);
- //player was stopped during activation, ask to stop it now
- //TODO: Handle this transition!
- //fsm.createTransition(SIGNAL_STARTED, STATE_IDLE, STATE_IDLE).setHandler(new StopRequest());
- //Player release, silently go to INVALID state
- fsm.createTransition(SIGNAL_RELEASE, STATE_IDLE, STATE_INVALID);
- //state ACTIVATNG
- //server said - ok
- fsm.createTransition(SIGNAL_STARTED, STATE_ACTIVATING, STATE_ACTIVE);
- //server said follow to the hand.
- fsm.createTransition(SIGNAL_FAILED, STATE_ACTIVATING, STATE_IDLE);
- //user asks to stop player
- fsm.createTransition(SIGNAL_STOP, STATE_ACTIVATING, STATE_IDLE);
- //Player has been released. Do not forget to notify server
- fsm.createTransition(SIGNAL_RELEASE, STATE_ACTIVATING, STATE_INVALID).setHandler(new StopRequest(this));
-
- //state ACTIVE
- //play finishes normaly
- fsm.createTransition(SIGNAL_PLAY_COMPLETED, STATE_ACTIVE, STATE_IDLE).setHandler(new PlayCompleted(this));
- //user asks to stop player
- fsm.createTransition(SIGNAL_STOP, STATE_ACTIVE, STATE_IDLE).setHandler(new StopRequest(this));
- //playback failure
- fsm.createTransition("FAILURE", STATE_ACTIVE, STATE_IDLE).setHandler(new PlayFailure(this));
- //Player has been released. Do not forget to notify server
- fsm.createTransition(SIGNAL_RELEASE, STATE_ACTIVE, STATE_INVALID).setHandler(new StopRequest(this));
- //state PAUSED
- //user ask to stop player but it was in puased mode so no need to notify server
- fsm.createTransition(SIGNAL_STOP, STATE_PAUSED, STATE_IDLE);
- //this transition means that some trigger activated. do not send anything more to server
- fsm.createTransition(SIGNAL_RESUME, STATE_PAUSED, STATE_ACTIVE);
- //Player has been released.
- fsm.createTransition(SIGNAL_RELEASE, STATE_INVALID, STATE_INVALID);
-
- try {
- fsm.signal(SIGNAL_CREATE);
- } catch (UnknownTransitionException e) {
- }
-
- }
-
- // Player methods
- @SuppressWarnings("static-access")
- public void play(URI[] uris, RTC[] rtc, Parameters params) throws MsControlException {
- if (rtc != null) {
- verifyRTC(rtc);
- }
-
- this.checkURI(uris);
- PlayTask task = new PlayTask(uris, rtc, params);
-
- //user calls play during the current work?
- if (fsm.getState().getName().equals(STATE_IDLE)) {
- playList.offer(task);
- } else {
- //no specific behaviour?
- if (params == null) {
- throw new MsControlException("Busy now: state=" + fsm.getState());
- }
-
- Value behaviour = (Value) params.get(Player.BEHAVIOUR_IF_BUSY);
- playList.offer(task);
- if (Player.FAIL_IF_BUSY.equals(behaviour)) {
- //queue task and send stop
- throw new MsControlException("Busy now");
- }
-
- if (Player.STOP_IF_BUSY.equals(behaviour)) {
- //queue task and send stop
- try {
- fsm.signal(SIGNAL_STOP);
- } catch (UnknownTransitionException e) {
- }
- }
- }
- new Thread(new Starter()).start();
-
- lock.lock();
- try {
- try {
- started.await();
- } catch (InterruptedException e) {
- }
- } finally {
- lock.unlock();
- }
- }
- @SuppressWarnings("static-access")
- public void play(URI uri, RTC[] rtc, Parameters params) throws MsControlException {
- play(new URI[]{uri}, rtc, params);
- }
- // Resource Methods
- public MediaGroup getContainer() {
- return this.parent;
- }
- // MediaEventNotifier methods
- public void addListener(MediaEventListener<PlayerEvent> listener) {
- this.listeners.add(listener);
- }
- public void removeListener(MediaEventListener<PlayerEvent> listener) {
- this.listeners.remove(listener);
- }
- public MediaSession getMediaSession() {
- return this.parent.getMediaSession();
- }
- protected void update(PlayerEvent anEvent) {
- }
- private void checkURI(URI[] uris) throws MsControlException {
- if (uris == null) {
- throw new MsControlException("URI[] cannot be null");
- }
- for (URI uri : uris) {
- if (uri == null) {
- throw new MsControlException("URI cannot be null");
- }
- if (uri.getScheme().equalsIgnoreCase("data")) {
- continue;
- }
- }
- }
- public void stop(boolean stopAll) {
- try {
- fsm.signal(SIGNAL_STOP);
- } catch (UnknownTransitionException e) {
- }
- }
-
- private void verifyRTC(RTC[] rtc) throws UnsupportedException {
- for (RTC r: rtc ) {
- if (r.getTrigger() == Player.PLAY_START && r.getAction() == Player.STOP) {
- throw new UnsupportedException("Invalid RTC");
- }
- }
- }
-
- protected void fireEvent(PlayerEvent event) {
- new Thread(new EventSender(event)).start();
- }
-
-
- private String createParams(URI[] uris, Parameters params) {
- StringBuilder buff = new StringBuilder();
-
- //add URI list of the player
- if (uris.length == 1) {
- buff.append("an=").append(uris[0].toString());
- } else {
- buff.append("an=").append(uris[0].toString());
- for (int i = 1; i < uris.length; i++) {
- buff.append(";");
- buff.append(uris[i].toString());
- }
- }
-
- this.qualifier = PlayerEvent.END_OF_PLAY_LIST;
-
- //max duraion if specified
- if (params != null && params.containsKey(Player.MAX_DURATION)) {
- long du = ((Integer)params.get(Player.MAX_DURATION));
- buff.append(" ");
- buff.append("du=").append(du);
-
- if (du > 0) {
- this.qualifier = PlayerEvent.DURATION_EXCEEDED;
- }
-
- this.reqDuration = du;
- }
- //start offset if specified
- if (params != null && params.containsKey(Player.START_OFFSET)) {
- buff.append(" ");
- buff.append("of=").append(params.get(Player.START_OFFSET));
- }
-
- //iterations if specified
- if (params != null && params.containsKey(Player.REPEAT_COUNT)) {
- int it = 0;
-
- if (params.get(Player.REPEAT_COUNT) instanceof Integer) {
- try {
- it = ((Integer)params.get(Player.REPEAT_COUNT));
- } catch (Exception e) {
- }
- }
-
- if (it < 0) it = 0;
- buff.append(" ");
- buff.append("it=").append(it);
- }
- //intervals if specified
- if (params != null && params.containsKey(Player.INTERVAL)) {
- buff.append(" ");
- buff.append("iv=").append(params.get(Player.INTERVAL));
- }
-
- return buff.toString();
- }
-
- private void requestAnnouncement() {
- //generate request identifier and transaction ID
- RequestIdentifier reqID = parent.nextRequestID();
- int txID = parent.getMediaSession().getDriver().getNextTxID();
-
- //constructs request
- NotificationRequest req = new NotificationRequest(this, parent.getEndpoint().getIdentifier(), reqID);
-
- RequestedAction[] actions = new RequestedAction[] { RequestedAction.NotifyImmediately };
- ArrayList<EventName> signalList = new ArrayList();
- ArrayList<RequestedEvent> eventList = new ArrayList();
- //player signals
- signalList.add(new EventName(PackageAU.Name, PackageAU.pa.withParm(params)));
- //player events
- eventList.add(new RequestedEvent(new EventName(PackageAU.Name, MgcpEvent.oc), actions));
- eventList.add(new RequestedEvent(new EventName(PackageAU.Name, MgcpEvent.of), actions));
-
- EventName[] signals = new EventName[signalList.size()];
- signalList.toArray(signals);
-
- RequestedEvent[] events = new RequestedEvent[eventList.size()];
- eventList.toArray(events);
-
- //new EventName(auPackageName, MgcpEvent.oc, connId)
- req.setRequestedEvents(events);
- req.setSignalRequests(signals);
-
- req.setTransactionHandle(txID);
- req.setNotifiedEntity(parent.getMediaSession().getDriver().getCallAgent());
- parent.getMediaSession().getDriver().attach(txID, this);
- parent.getMediaSession().getDriver().attach(reqID, this);
-
- parent.getMediaSession().getDriver().send(req);
- }
- private void requestStop() {
- //generate request identifier and transaction ID
- RequestIdentifier reqID = parent.nextRequestID();
- int txID = parent.getMediaSession().getDriver().getNextTxID();
-
- //constructs request
- NotificationRequest req = new NotificationRequest(this, parent.getEndpoint().getIdentifier(), reqID);
-
- req.setTransactionHandle(txID);
- req.setNotifiedEntity(parent.getMediaSession().getDriver().getCallAgent());
- parent.getMediaSession().getDriver().attach(txID, this);
- parent.getMediaSession().getDriver().attach(reqID, this);
-
- parent.getMediaSession().getDriver().send(req);
- }
-
- /**
- * Fires event.
- *
- * @param evt the event to be fired
- */
- private void fireEvent(EventName eventName) {
- this.returnCode = eventName.getEventIdentifier().getParms();
- switch (eventName.getEventIdentifier().intValue()) {
- case MgcpEvent.REPORT_ON_COMPLETION :
- try {
- fsm.signal(SIGNAL_PLAY_COMPLETED);
- } catch (UnknownTransitionException e) {
- e.printStackTrace();
- }
- break;
- case MgcpEvent.REPORT_FAILURE :
- try {
- fsm.signal("FAILURE");
- } catch (UnknownTransitionException e) {
- e.printStackTrace();
- }
- break;
- }
-
- }
-
- private class PlayRequest implements TransitionHandler {
- public void process(State state) {
- requestAnnouncement();
- }
-
- }
- private class PlayCompleted implements TransitionHandler {
- private PlayerImpl player;
-
- protected PlayCompleted(PlayerImpl player) {
- this.player = player;
- }
-
- public void process(State state) {
- stopTime = System.currentTimeMillis();
- if (Math.abs(stopTime - startTime - reqDuration) > 1000) {
- qualifier = PlayerEvent.END_OF_PLAY_LIST;
- }
-
- PlayerEventImpl evt = new PlayerEventImpl(player, PlayerEvent.PLAY_COMPLETED, true, qualifier, null);
- evt.setOffset((int)(stopTime - startTime));
- fireEvent(evt);
-
- if (!playList.isEmpty()) {
- new Thread(new Starter()).start();
- }
- }
-
- }
- private class PlayFailure implements TransitionHandler {
- private PlayerImpl player;
-
- protected PlayFailure(PlayerImpl player) {
- this.player = player;
- }
-
- public void process(State state) {
- MediaErr error = null;
-
- if (returnCode.equals("rc=301")) {
- error = MediaErr.BAD_ARG;
- } else if (returnCode.equals("rc=312")) {
- error = MediaErr.NOT_FOUND;
- }
-
- stopTime = System.currentTimeMillis();
- PlayerEventImpl evt = new PlayerEventImpl(player, PlayerEvent.PLAY_COMPLETED, false, PlayerEvent.NO_QUALIFIER, null, error, "");
- evt.setOffset((int)(stopTime - startTime));
- fireEvent(evt);
-
- if (!playList.isEmpty()) {
- new Thread(new Starter()).start();
- }
- }
-
- }
-
- private class StopRequest implements TransitionHandler {
- private PlayerImpl player;
-
- protected StopRequest(PlayerImpl player) {
- this.player = player;
- }
- public void process(State state) {
- requestStop();
- PlayerEventImpl evt = new PlayerEventImpl(player, PlayerEvent.PLAY_COMPLETED, true, PlayerEvent.STOPPED, null);
- evt.setOffset((int)(stopTime - startTime));
- fireEvent(evt);
- }
-
- }
-
- private class OnStart implements StateEventHandler {
- public void onEvent(State state) {
- startTime = System.currentTimeMillis() + timeError;
- //notify that player started
- lock.lock();
- try {
- started.signal();
- } finally {
- lock.unlock();
- }
- }
-
- }
- public void processMgcpCommandEvent(JainMgcpCommandEvent event) {
- switch (event.getObjectIdentifier()) {
- case Constants.CMD_NOTIFY :
- Notify notify = (Notify) event;
-
- EventName[] events = notify.getObservedEvents();
- for (EventName evt: events) {
- fireEvent(evt);
- }
-
- break;
- default :
- return;
- }
- }
- public void processMgcpResponseEvent(JainMgcpResponseEvent event) {
- switch (event.getReturnCode().getValue()) {
- case ReturnCode.TRANSACTION_BEING_EXECUTED :
- break;
- case ReturnCode.TRANSACTION_EXECUTED_NORMALLY :
- try {
- fsm.signal(SIGNAL_STARTED);
- } catch (UnknownTransitionException e) {
- }
- break;
- default :
- try {
- fsm.signal(SIGNAL_FAILED);
- } catch (UnknownTransitionException e) {
- }
- }
- }
-
- private class PlayTask {
- private URI[] uris;
- private RTC[] rtc;
- private Parameters params;
-
- protected PlayTask(URI[] uris, RTC[] rtc, Parameters params) {
- this.uris = uris;
- this.rtc = rtc;
- this.params = params;
- }
- }
-
- private class Starter implements Runnable {
- public void run() {
- PlayTask task = playList.poll();
- params = createParams(task.uris, task.params);
-
- try {
- fsm.signal(SIGNAL_PLAY);
- } catch (UnknownTransitionException e) {
- }
- }
-
- }
- private class EventSender implements Runnable {
-
- private PlayerEvent evt;
-
- public EventSender(PlayerEvent evt) {
- this.evt = evt;
- }
-
- public void run() {
- for (MediaEventListener l : listeners) {
- l.onEvent(evt);
- }
- }
- }
-
- public void info(String s) {
- parent.info(s);
- }
- public void debug(String s) {
- parent.debug(s);
- }
- public void warn(String s) {
- parent.warn(s);
- }
- }