PageRenderTime 28ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/servers/media/jsr-309/src/main/java/org/mobicents/javax/media/mscontrol/mediagroup/PlayerImpl.java

http://mobicents.googlecode.com/
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
  1. package org.mobicents.javax.media.mscontrol.mediagroup;
  2. import jain.protocol.ip.mgcp.JainMgcpCommandEvent;
  3. import jain.protocol.ip.mgcp.JainMgcpListener;
  4. import jain.protocol.ip.mgcp.JainMgcpResponseEvent;
  5. import jain.protocol.ip.mgcp.message.Constants;
  6. import jain.protocol.ip.mgcp.message.NotificationRequest;
  7. import jain.protocol.ip.mgcp.message.Notify;
  8. import jain.protocol.ip.mgcp.message.parms.EventName;
  9. import jain.protocol.ip.mgcp.message.parms.RequestIdentifier;
  10. import jain.protocol.ip.mgcp.message.parms.RequestedAction;
  11. import jain.protocol.ip.mgcp.message.parms.RequestedEvent;
  12. import jain.protocol.ip.mgcp.message.parms.ReturnCode;
  13. import jain.protocol.ip.mgcp.pkg.MgcpEvent;
  14. import java.net.URI;
  15. import java.util.ArrayList;
  16. import java.util.concurrent.ConcurrentLinkedQueue;
  17. import java.util.concurrent.CopyOnWriteArrayList;
  18. import java.util.concurrent.locks.Condition;
  19. import java.util.concurrent.locks.ReentrantLock;
  20. import javax.media.mscontrol.MediaErr;
  21. import javax.media.mscontrol.MediaEventListener;
  22. import javax.media.mscontrol.MediaSession;
  23. import javax.media.mscontrol.MsControlException;
  24. import javax.media.mscontrol.Parameters;
  25. import javax.media.mscontrol.Qualifier;
  26. import javax.media.mscontrol.UnsupportedException;
  27. import javax.media.mscontrol.Value;
  28. import javax.media.mscontrol.mediagroup.MediaGroup;
  29. import javax.media.mscontrol.mediagroup.Player;
  30. import javax.media.mscontrol.mediagroup.PlayerEvent;
  31. import javax.media.mscontrol.resource.RTC;
  32. import org.mobicents.fsm.FSM;
  33. import org.mobicents.fsm.Logger;
  34. import org.mobicents.fsm.State;
  35. import org.mobicents.fsm.StateEventHandler;
  36. import org.mobicents.fsm.TransitionHandler;
  37. import org.mobicents.fsm.UnknownTransitionException;
  38. import org.mobicents.jsr309.mgcp.PackageAU;
  39. /**
  40. * b
  41. * @author amit bhayani
  42. *
  43. */
  44. public class PlayerImpl implements Player, JainMgcpListener, Logger {
  45. public final static String STATE_NULL = "NULL";
  46. public final static String STATE_IDLE = "IDLE";
  47. public final static String STATE_ACTIVE = "ACTIVE";
  48. public final static String STATE_ACTIVATING = "ACTIVATING";
  49. public final static String STATE_PAUSED = "PAUSED";
  50. public final static String STATE_INVALID = "INVALID";
  51. public final static String SIGNAL_CREATE = "CREATE";
  52. public final static String SIGNAL_PLAY = "PLAY";
  53. public final static String SIGNAL_STARTED = "STARTED";
  54. public final static String SIGNAL_FAILED = "FAILED";
  55. public final static String SIGNAL_START_PAUSED = "START_PAUSED";
  56. public final static String SIGNAL_STOP = "STOP";
  57. public final static String SIGNAL_PAUSE = "PAUSE";
  58. public final static String SIGNAL_RESUME = "RESUME";
  59. public final static String SIGNAL_PLAY_COMPLETED = "PLAY_COMPLETED";
  60. public final static String SIGNAL_RELEASE = "RELEASE";
  61. protected MediaGroupImpl parent = null;
  62. private FSM fsm;
  63. protected CopyOnWriteArrayList<MediaEventListener<PlayerEvent>> listeners = new CopyOnWriteArrayList<MediaEventListener<PlayerEvent>>();
  64. protected String uri;
  65. private String params;
  66. private String returnCode;
  67. private Qualifier qualifier;
  68. private ReentrantLock lock = new ReentrantLock();
  69. private Condition started = lock.newCondition();
  70. private long startTime;
  71. private long timeError = 100;
  72. private long stopTime;
  73. private long reqDuration;
  74. private ConcurrentLinkedQueue<PlayTask> playList = new ConcurrentLinkedQueue();
  75. //-------------------------------------------------------------------------------------------/
  76. protected PlayerImpl(MediaGroupImpl parent) throws MsControlException {
  77. this.parent = parent;
  78. this.initFSM();
  79. }
  80. private void initFSM() {
  81. fsm = new FSM(parent.getMediaSession().getDriver().getScheduler());
  82. fsm.setLogger(this);
  83. fsm.createState(STATE_NULL);
  84. fsm.createState(STATE_IDLE);
  85. fsm.createState(STATE_ACTIVATING);
  86. fsm.createState(STATE_ACTIVE).setOnEnter(new OnStart());
  87. fsm.createState(STATE_PAUSED);
  88. fsm.createState(STATE_INVALID);
  89. fsm.setStart(STATE_NULL);
  90. fsm.setEnd(STATE_INVALID);
  91. //state NULL
  92. fsm.createTransition(SIGNAL_CREATE, STATE_NULL, STATE_IDLE);
  93. fsm.createTransition(SIGNAL_RELEASE, STATE_NULL, STATE_INVALID);
  94. //state IDLE
  95. //sending request to server and waiting response
  96. fsm.createTransition(SIGNAL_PLAY, STATE_IDLE, STATE_ACTIVATING).setHandler(new PlayRequest());
  97. //player started in suspended mode, nothing is sent to server in this case
  98. fsm.createTransition(SIGNAL_START_PAUSED, STATE_IDLE, STATE_PAUSED);
  99. //player was stopped during activation, ask to stop it now
  100. //TODO: Handle this transition!
  101. //fsm.createTransition(SIGNAL_STARTED, STATE_IDLE, STATE_IDLE).setHandler(new StopRequest());
  102. //Player release, silently go to INVALID state
  103. fsm.createTransition(SIGNAL_RELEASE, STATE_IDLE, STATE_INVALID);
  104. //state ACTIVATNG
  105. //server said - ok
  106. fsm.createTransition(SIGNAL_STARTED, STATE_ACTIVATING, STATE_ACTIVE);
  107. //server said follow to the hand.
  108. fsm.createTransition(SIGNAL_FAILED, STATE_ACTIVATING, STATE_IDLE);
  109. //user asks to stop player
  110. fsm.createTransition(SIGNAL_STOP, STATE_ACTIVATING, STATE_IDLE);
  111. //Player has been released. Do not forget to notify server
  112. fsm.createTransition(SIGNAL_RELEASE, STATE_ACTIVATING, STATE_INVALID).setHandler(new StopRequest(this));
  113. //state ACTIVE
  114. //play finishes normaly
  115. fsm.createTransition(SIGNAL_PLAY_COMPLETED, STATE_ACTIVE, STATE_IDLE).setHandler(new PlayCompleted(this));
  116. //user asks to stop player
  117. fsm.createTransition(SIGNAL_STOP, STATE_ACTIVE, STATE_IDLE).setHandler(new StopRequest(this));
  118. //playback failure
  119. fsm.createTransition("FAILURE", STATE_ACTIVE, STATE_IDLE).setHandler(new PlayFailure(this));
  120. //Player has been released. Do not forget to notify server
  121. fsm.createTransition(SIGNAL_RELEASE, STATE_ACTIVE, STATE_INVALID).setHandler(new StopRequest(this));
  122. //state PAUSED
  123. //user ask to stop player but it was in puased mode so no need to notify server
  124. fsm.createTransition(SIGNAL_STOP, STATE_PAUSED, STATE_IDLE);
  125. //this transition means that some trigger activated. do not send anything more to server
  126. fsm.createTransition(SIGNAL_RESUME, STATE_PAUSED, STATE_ACTIVE);
  127. //Player has been released.
  128. fsm.createTransition(SIGNAL_RELEASE, STATE_INVALID, STATE_INVALID);
  129. try {
  130. fsm.signal(SIGNAL_CREATE);
  131. } catch (UnknownTransitionException e) {
  132. }
  133. }
  134. // Player methods
  135. @SuppressWarnings("static-access")
  136. public void play(URI[] uris, RTC[] rtc, Parameters params) throws MsControlException {
  137. if (rtc != null) {
  138. verifyRTC(rtc);
  139. }
  140. this.checkURI(uris);
  141. PlayTask task = new PlayTask(uris, rtc, params);
  142. //user calls play during the current work?
  143. if (fsm.getState().getName().equals(STATE_IDLE)) {
  144. playList.offer(task);
  145. } else {
  146. //no specific behaviour?
  147. if (params == null) {
  148. throw new MsControlException("Busy now: state=" + fsm.getState());
  149. }
  150. Value behaviour = (Value) params.get(Player.BEHAVIOUR_IF_BUSY);
  151. playList.offer(task);
  152. if (Player.FAIL_IF_BUSY.equals(behaviour)) {
  153. //queue task and send stop
  154. throw new MsControlException("Busy now");
  155. }
  156. if (Player.STOP_IF_BUSY.equals(behaviour)) {
  157. //queue task and send stop
  158. try {
  159. fsm.signal(SIGNAL_STOP);
  160. } catch (UnknownTransitionException e) {
  161. }
  162. }
  163. }
  164. new Thread(new Starter()).start();
  165. lock.lock();
  166. try {
  167. try {
  168. started.await();
  169. } catch (InterruptedException e) {
  170. }
  171. } finally {
  172. lock.unlock();
  173. }
  174. }
  175. @SuppressWarnings("static-access")
  176. public void play(URI uri, RTC[] rtc, Parameters params) throws MsControlException {
  177. play(new URI[]{uri}, rtc, params);
  178. }
  179. // Resource Methods
  180. public MediaGroup getContainer() {
  181. return this.parent;
  182. }
  183. // MediaEventNotifier methods
  184. public void addListener(MediaEventListener<PlayerEvent> listener) {
  185. this.listeners.add(listener);
  186. }
  187. public void removeListener(MediaEventListener<PlayerEvent> listener) {
  188. this.listeners.remove(listener);
  189. }
  190. public MediaSession getMediaSession() {
  191. return this.parent.getMediaSession();
  192. }
  193. protected void update(PlayerEvent anEvent) {
  194. }
  195. private void checkURI(URI[] uris) throws MsControlException {
  196. if (uris == null) {
  197. throw new MsControlException("URI[] cannot be null");
  198. }
  199. for (URI uri : uris) {
  200. if (uri == null) {
  201. throw new MsControlException("URI cannot be null");
  202. }
  203. if (uri.getScheme().equalsIgnoreCase("data")) {
  204. continue;
  205. }
  206. }
  207. }
  208. public void stop(boolean stopAll) {
  209. try {
  210. fsm.signal(SIGNAL_STOP);
  211. } catch (UnknownTransitionException e) {
  212. }
  213. }
  214. private void verifyRTC(RTC[] rtc) throws UnsupportedException {
  215. for (RTC r: rtc ) {
  216. if (r.getTrigger() == Player.PLAY_START && r.getAction() == Player.STOP) {
  217. throw new UnsupportedException("Invalid RTC");
  218. }
  219. }
  220. }
  221. protected void fireEvent(PlayerEvent event) {
  222. new Thread(new EventSender(event)).start();
  223. }
  224. private String createParams(URI[] uris, Parameters params) {
  225. StringBuilder buff = new StringBuilder();
  226. //add URI list of the player
  227. if (uris.length == 1) {
  228. buff.append("an=").append(uris[0].toString());
  229. } else {
  230. buff.append("an=").append(uris[0].toString());
  231. for (int i = 1; i < uris.length; i++) {
  232. buff.append(";");
  233. buff.append(uris[i].toString());
  234. }
  235. }
  236. this.qualifier = PlayerEvent.END_OF_PLAY_LIST;
  237. //max duraion if specified
  238. if (params != null && params.containsKey(Player.MAX_DURATION)) {
  239. long du = ((Integer)params.get(Player.MAX_DURATION));
  240. buff.append(" ");
  241. buff.append("du=").append(du);
  242. if (du > 0) {
  243. this.qualifier = PlayerEvent.DURATION_EXCEEDED;
  244. }
  245. this.reqDuration = du;
  246. }
  247. //start offset if specified
  248. if (params != null && params.containsKey(Player.START_OFFSET)) {
  249. buff.append(" ");
  250. buff.append("of=").append(params.get(Player.START_OFFSET));
  251. }
  252. //iterations if specified
  253. if (params != null && params.containsKey(Player.REPEAT_COUNT)) {
  254. int it = 0;
  255. if (params.get(Player.REPEAT_COUNT) instanceof Integer) {
  256. try {
  257. it = ((Integer)params.get(Player.REPEAT_COUNT));
  258. } catch (Exception e) {
  259. }
  260. }
  261. if (it < 0) it = 0;
  262. buff.append(" ");
  263. buff.append("it=").append(it);
  264. }
  265. //intervals if specified
  266. if (params != null && params.containsKey(Player.INTERVAL)) {
  267. buff.append(" ");
  268. buff.append("iv=").append(params.get(Player.INTERVAL));
  269. }
  270. return buff.toString();
  271. }
  272. private void requestAnnouncement() {
  273. //generate request identifier and transaction ID
  274. RequestIdentifier reqID = parent.nextRequestID();
  275. int txID = parent.getMediaSession().getDriver().getNextTxID();
  276. //constructs request
  277. NotificationRequest req = new NotificationRequest(this, parent.getEndpoint().getIdentifier(), reqID);
  278. RequestedAction[] actions = new RequestedAction[] { RequestedAction.NotifyImmediately };
  279. ArrayList<EventName> signalList = new ArrayList();
  280. ArrayList<RequestedEvent> eventList = new ArrayList();
  281. //player signals
  282. signalList.add(new EventName(PackageAU.Name, PackageAU.pa.withParm(params)));
  283. //player events
  284. eventList.add(new RequestedEvent(new EventName(PackageAU.Name, MgcpEvent.oc), actions));
  285. eventList.add(new RequestedEvent(new EventName(PackageAU.Name, MgcpEvent.of), actions));
  286. EventName[] signals = new EventName[signalList.size()];
  287. signalList.toArray(signals);
  288. RequestedEvent[] events = new RequestedEvent[eventList.size()];
  289. eventList.toArray(events);
  290. //new EventName(auPackageName, MgcpEvent.oc, connId)
  291. req.setRequestedEvents(events);
  292. req.setSignalRequests(signals);
  293. req.setTransactionHandle(txID);
  294. req.setNotifiedEntity(parent.getMediaSession().getDriver().getCallAgent());
  295. parent.getMediaSession().getDriver().attach(txID, this);
  296. parent.getMediaSession().getDriver().attach(reqID, this);
  297. parent.getMediaSession().getDriver().send(req);
  298. }
  299. private void requestStop() {
  300. //generate request identifier and transaction ID
  301. RequestIdentifier reqID = parent.nextRequestID();
  302. int txID = parent.getMediaSession().getDriver().getNextTxID();
  303. //constructs request
  304. NotificationRequest req = new NotificationRequest(this, parent.getEndpoint().getIdentifier(), reqID);
  305. req.setTransactionHandle(txID);
  306. req.setNotifiedEntity(parent.getMediaSession().getDriver().getCallAgent());
  307. parent.getMediaSession().getDriver().attach(txID, this);
  308. parent.getMediaSession().getDriver().attach(reqID, this);
  309. parent.getMediaSession().getDriver().send(req);
  310. }
  311. /**
  312. * Fires event.
  313. *
  314. * @param evt the event to be fired
  315. */
  316. private void fireEvent(EventName eventName) {
  317. this.returnCode = eventName.getEventIdentifier().getParms();
  318. switch (eventName.getEventIdentifier().intValue()) {
  319. case MgcpEvent.REPORT_ON_COMPLETION :
  320. try {
  321. fsm.signal(SIGNAL_PLAY_COMPLETED);
  322. } catch (UnknownTransitionException e) {
  323. e.printStackTrace();
  324. }
  325. break;
  326. case MgcpEvent.REPORT_FAILURE :
  327. try {
  328. fsm.signal("FAILURE");
  329. } catch (UnknownTransitionException e) {
  330. e.printStackTrace();
  331. }
  332. break;
  333. }
  334. }
  335. private class PlayRequest implements TransitionHandler {
  336. public void process(State state) {
  337. requestAnnouncement();
  338. }
  339. }
  340. private class PlayCompleted implements TransitionHandler {
  341. private PlayerImpl player;
  342. protected PlayCompleted(PlayerImpl player) {
  343. this.player = player;
  344. }
  345. public void process(State state) {
  346. stopTime = System.currentTimeMillis();
  347. if (Math.abs(stopTime - startTime - reqDuration) > 1000) {
  348. qualifier = PlayerEvent.END_OF_PLAY_LIST;
  349. }
  350. PlayerEventImpl evt = new PlayerEventImpl(player, PlayerEvent.PLAY_COMPLETED, true, qualifier, null);
  351. evt.setOffset((int)(stopTime - startTime));
  352. fireEvent(evt);
  353. if (!playList.isEmpty()) {
  354. new Thread(new Starter()).start();
  355. }
  356. }
  357. }
  358. private class PlayFailure implements TransitionHandler {
  359. private PlayerImpl player;
  360. protected PlayFailure(PlayerImpl player) {
  361. this.player = player;
  362. }
  363. public void process(State state) {
  364. MediaErr error = null;
  365. if (returnCode.equals("rc=301")) {
  366. error = MediaErr.BAD_ARG;
  367. } else if (returnCode.equals("rc=312")) {
  368. error = MediaErr.NOT_FOUND;
  369. }
  370. stopTime = System.currentTimeMillis();
  371. PlayerEventImpl evt = new PlayerEventImpl(player, PlayerEvent.PLAY_COMPLETED, false, PlayerEvent.NO_QUALIFIER, null, error, "");
  372. evt.setOffset((int)(stopTime - startTime));
  373. fireEvent(evt);
  374. if (!playList.isEmpty()) {
  375. new Thread(new Starter()).start();
  376. }
  377. }
  378. }
  379. private class StopRequest implements TransitionHandler {
  380. private PlayerImpl player;
  381. protected StopRequest(PlayerImpl player) {
  382. this.player = player;
  383. }
  384. public void process(State state) {
  385. requestStop();
  386. PlayerEventImpl evt = new PlayerEventImpl(player, PlayerEvent.PLAY_COMPLETED, true, PlayerEvent.STOPPED, null);
  387. evt.setOffset((int)(stopTime - startTime));
  388. fireEvent(evt);
  389. }
  390. }
  391. private class OnStart implements StateEventHandler {
  392. public void onEvent(State state) {
  393. startTime = System.currentTimeMillis() + timeError;
  394. //notify that player started
  395. lock.lock();
  396. try {
  397. started.signal();
  398. } finally {
  399. lock.unlock();
  400. }
  401. }
  402. }
  403. public void processMgcpCommandEvent(JainMgcpCommandEvent event) {
  404. switch (event.getObjectIdentifier()) {
  405. case Constants.CMD_NOTIFY :
  406. Notify notify = (Notify) event;
  407. EventName[] events = notify.getObservedEvents();
  408. for (EventName evt: events) {
  409. fireEvent(evt);
  410. }
  411. break;
  412. default :
  413. return;
  414. }
  415. }
  416. public void processMgcpResponseEvent(JainMgcpResponseEvent event) {
  417. switch (event.getReturnCode().getValue()) {
  418. case ReturnCode.TRANSACTION_BEING_EXECUTED :
  419. break;
  420. case ReturnCode.TRANSACTION_EXECUTED_NORMALLY :
  421. try {
  422. fsm.signal(SIGNAL_STARTED);
  423. } catch (UnknownTransitionException e) {
  424. }
  425. break;
  426. default :
  427. try {
  428. fsm.signal(SIGNAL_FAILED);
  429. } catch (UnknownTransitionException e) {
  430. }
  431. }
  432. }
  433. private class PlayTask {
  434. private URI[] uris;
  435. private RTC[] rtc;
  436. private Parameters params;
  437. protected PlayTask(URI[] uris, RTC[] rtc, Parameters params) {
  438. this.uris = uris;
  439. this.rtc = rtc;
  440. this.params = params;
  441. }
  442. }
  443. private class Starter implements Runnable {
  444. public void run() {
  445. PlayTask task = playList.poll();
  446. params = createParams(task.uris, task.params);
  447. try {
  448. fsm.signal(SIGNAL_PLAY);
  449. } catch (UnknownTransitionException e) {
  450. }
  451. }
  452. }
  453. private class EventSender implements Runnable {
  454. private PlayerEvent evt;
  455. public EventSender(PlayerEvent evt) {
  456. this.evt = evt;
  457. }
  458. public void run() {
  459. for (MediaEventListener l : listeners) {
  460. l.onEvent(evt);
  461. }
  462. }
  463. }
  464. public void info(String s) {
  465. parent.info(s);
  466. }
  467. public void debug(String s) {
  468. parent.debug(s);
  469. }
  470. public void warn(String s) {
  471. parent.warn(s);
  472. }
  473. }