PageRenderTime 45ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://mobicents.googlecode.com/
Java | 450 lines | 334 code | 86 blank | 30 comment | 64 complexity | b8dd600794969728a762b035ecb7c71b 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.CopyOnWriteArrayList;
  17. import javax.media.mscontrol.MediaErr;
  18. import javax.media.mscontrol.MediaEvent;
  19. import javax.media.mscontrol.MediaEventListener;
  20. import javax.media.mscontrol.MediaSession;
  21. import javax.media.mscontrol.MsControlException;
  22. import javax.media.mscontrol.Parameters;
  23. import javax.media.mscontrol.UnsupportedException;
  24. import javax.media.mscontrol.join.Joinable.Direction;
  25. import javax.media.mscontrol.mediagroup.MediaGroup;
  26. import javax.media.mscontrol.mediagroup.Player;
  27. import javax.media.mscontrol.mediagroup.Recorder;
  28. import javax.media.mscontrol.mediagroup.RecorderEvent;
  29. import javax.media.mscontrol.mediagroup.SpeechDetectorConstants;
  30. import javax.media.mscontrol.mediagroup.signals.SignalDetector;
  31. import javax.media.mscontrol.resource.RTC;
  32. import javax.media.mscontrol.resource.Resource;
  33. import javax.media.mscontrol.resource.Trigger;
  34. import org.mobicents.fsm.FSM;
  35. import org.mobicents.fsm.State;
  36. import org.mobicents.fsm.StateEventHandler;
  37. import org.mobicents.fsm.TransitionHandler;
  38. import org.mobicents.fsm.UnknownTransitionException;
  39. import org.mobicents.javax.media.mscontrol.mediagroup.signals.Options;
  40. import org.mobicents.jsr309.mgcp.PackageAU;
  41. /**
  42. *
  43. * @author amit bhayani
  44. * @author kulikov
  45. * @author Jose Antonio Santos Cadenas
  46. */
  47. public class RecorderImpl implements Recorder, JainMgcpListener {
  48. public final static String STATE_IDLE = "IDLE";
  49. public final static String STATE_ACTIVATING = "ACTIVATING";
  50. public final static String STATE_ACTIVE = "ACTIVE";
  51. public final static String STATE_PAUSED = "PAUSED";
  52. public final static String STATE_STOPPING = "STOPPING";
  53. public final static String SIGNAL_START = "start";
  54. public final static String SIGNAL_START_PAUSED = "start_paused";
  55. public final static String SIGNAL_STOP = "stop";
  56. public final static String SIGNAL_PAUSE = "pause";
  57. public final static String SIGNAL_RESUME = "resume";
  58. public final static String SIGNAL_SUCCESS = "success";
  59. public final static String SIGNAL_FAILURE = "failure";
  60. public final static String SIGNAL_COMPLETE = "complete";
  61. protected MediaGroupImpl parent = null;
  62. private FSM fsm;
  63. private String params;
  64. protected CopyOnWriteArrayList<MediaEventListener<? extends MediaEvent<?>>> listeners = new CopyOnWriteArrayList<MediaEventListener<? extends MediaEvent<?>>>();
  65. private RecorderEventImpl recorderEvent;
  66. private boolean rtcTriggered = false;
  67. private ArrayList<Trigger> triggers = new ArrayList();
  68. public RecorderImpl(MediaGroupImpl mediaGroup) {
  69. this.parent = mediaGroup;
  70. initFSM();
  71. }
  72. private void initFSM() {
  73. fsm = new FSM(parent.getMediaSession().getDriver().getScheduler());
  74. fsm.createState(STATE_IDLE);
  75. fsm.createState(STATE_ACTIVATING);
  76. fsm.createState(STATE_ACTIVE);
  77. fsm.createState(STATE_PAUSED);
  78. fsm.createState(STATE_STOPPING).setOnEnter(new StopRequest());
  79. fsm.setStart(STATE_IDLE);
  80. fsm.setEnd(STATE_IDLE);
  81. //state IDLE
  82. fsm.createTransition(SIGNAL_START, STATE_IDLE, STATE_ACTIVATING).setHandler(new RecordRequest());
  83. fsm.createTransition(SIGNAL_START_PAUSED, STATE_IDLE, STATE_PAUSED);
  84. fsm.createTransition(SIGNAL_SUCCESS, STATE_ACTIVATING, STATE_ACTIVE);
  85. //server said follow to the hand.
  86. fsm.createTransition(SIGNAL_FAILURE, STATE_ACTIVATING, STATE_IDLE).setHandler(new CompleteNotify());
  87. //user has asked to stop player
  88. fsm.createTransition(SIGNAL_STOP, STATE_ACTIVATING, STATE_IDLE);
  89. //state ACTIVE
  90. fsm.createTransition(SIGNAL_STOP, STATE_ACTIVE, STATE_STOPPING);
  91. fsm.createTransition(SIGNAL_PAUSE, STATE_ACTIVE, STATE_PAUSED);
  92. fsm.createTransition(SIGNAL_COMPLETE, STATE_ACTIVE, STATE_IDLE).setHandler(new CompleteNotify());
  93. fsm.createTransition(SIGNAL_FAILURE, STATE_ACTIVE, STATE_IDLE);
  94. //state PAUSED
  95. fsm.createTransition(SIGNAL_STOP, STATE_PAUSED, STATE_IDLE);
  96. fsm.createTransition(SIGNAL_RESUME, STATE_PAUSED, STATE_ACTIVE);
  97. //state STOPPING
  98. fsm.createTransition(SIGNAL_SUCCESS, STATE_STOPPING, STATE_IDLE).setHandler(new StoppedNotify());
  99. }
  100. public void record(URI streamID, RTC[] rtc, Parameters optargs) throws MsControlException {
  101. if (rtc != null) {
  102. verifyRTC(rtc);
  103. }
  104. if (parent.getJoinees(Direction.RECV).length == 0) {
  105. throw new MsControlException(this.parent.getURI()
  106. + " Container is not joined to any other container");
  107. }
  108. String[] patterns = this.getPatterns(rtc, optargs);
  109. Options options = new Options();
  110. options.setRecordID(streamID.toString());
  111. //patterns
  112. if (patterns != null) {
  113. options.setDigitPattern(patterns);
  114. }
  115. //check for max duration
  116. if (optargs != null && optargs.containsKey(Recorder.MAX_DURATION)) {
  117. options.setRecordDuraion((Integer)optargs.get(Recorder.MAX_DURATION));
  118. }
  119. //check for append
  120. if (optargs != null && optargs.containsKey(Recorder.APPEND)) {
  121. options.setOverride(!(Boolean)optargs.get(Recorder.APPEND));
  122. }
  123. //post-speech time
  124. if (optargs != null && optargs.containsKey(Recorder.SILENCE_TERMINATION_ON)) {
  125. options.setSilenceTermination((Boolean)optargs.get(Recorder.SILENCE_TERMINATION_ON));
  126. }
  127. //final timeout
  128. if (optargs != null && optargs.containsKey(SpeechDetectorConstants.FINAL_TIMEOUT)) {
  129. options.setSilenceTermination(false);
  130. if (optargs.get(SpeechDetectorConstants.FINAL_TIMEOUT) != Resource.FOR_EVER) {
  131. options.setPostSpeechTimer((Integer)optargs.get(SpeechDetectorConstants.FINAL_TIMEOUT));
  132. }
  133. }
  134. //initial prompt
  135. if (optargs != null && optargs.containsKey(Recorder.PROMPT)) {
  136. options.setPrompt(((URI)optargs.get(Recorder.PROMPT)).toString());
  137. }
  138. options.setNonInterruptiblePlay(true);
  139. if (rtc != null) {
  140. System.out.println("-------------------------------------");
  141. System.out.println("triggers=" + rtc.length);
  142. for (int i = 0; i < rtc.length; i++) {
  143. System.out.println("Trigger = " + rtc[i].getTrigger());
  144. if (rtc[i] == MediaGroup.SIGDET_STOPPLAY) {
  145. options.setNonInterruptiblePlay(false);
  146. } else if (rtc[i].getTrigger() == SignalDetector.DETECTION_OF_ONE_SIGNAL) {
  147. options.setDigitsNumber(1);
  148. } else if (rtc[i].getTrigger() == Player.PLAY_START && rtc[i].getAction() == SignalDetector.FLUSH_BUFFER) {
  149. options.setClearDigits(true);
  150. }
  151. }
  152. }
  153. params = options.toString();
  154. try {
  155. fsm.signal(SIGNAL_START);
  156. } catch (UnknownTransitionException e) {
  157. throw new MsControlException(e.getMessage());
  158. }
  159. }
  160. private void verifyRTC(RTC[] rtc) throws UnsupportedException {
  161. for (RTC r: rtc ) {
  162. if (r.getTrigger() == Player.PLAY_START && r.getAction() == Player.STOP) {
  163. throw new UnsupportedException("Invalid RTC");
  164. }
  165. }
  166. }
  167. private String[] getPatterns(RTC[] rtc, Parameters options) {
  168. if (rtc == null || options == null) {
  169. return null;
  170. }
  171. ArrayList<String> list = new ArrayList();
  172. for (RTC r : rtc) {
  173. for (int i = 0; i < SignalDetector.PATTERN_MATCH.length; i++) {
  174. if (r.getTrigger() == SignalDetector.PATTERN_MATCH[i]) {
  175. if (options.containsKey(SignalDetector.PATTERN[i])) {
  176. String s = (String) options.get(SignalDetector.PATTERN[i]);
  177. String pattern = "";
  178. //now hack for TCK: test_2_1_9_7_MultipleReturnKeys
  179. for (int k = 0; k < s.length() - 1; k++) {
  180. pattern += (s.charAt(k) + "|");
  181. }
  182. pattern += s.charAt(s.length() - 1);
  183. list.add(pattern);
  184. triggers.add(SignalDetector.PATTERN_MATCH[i]);
  185. }
  186. }
  187. }
  188. }
  189. if (list.isEmpty()) {
  190. return null;
  191. }
  192. String[] patterns = new String[list.size()];
  193. list.toArray(patterns);
  194. this.rtcTriggered = true;
  195. return patterns;
  196. }
  197. public MediaGroup getContainer() {
  198. return this.parent;
  199. }
  200. public void stop() {
  201. try {
  202. fsm.signal(SIGNAL_STOP);
  203. } catch (UnknownTransitionException e) {
  204. }
  205. }
  206. public void addListener(MediaEventListener<RecorderEvent> listener) {
  207. this.listeners.add(listener);
  208. }
  209. public MediaSession getMediaSession() {
  210. return this.parent.getMediaSession();
  211. }
  212. public void removeListener(MediaEventListener<RecorderEvent> listener) {
  213. this.listeners.remove(listener);
  214. }
  215. protected void update(RecorderEvent anEvent) {
  216. for (MediaEventListener m : listeners) {
  217. m.onEvent(anEvent);
  218. }
  219. }
  220. private void requestRecording() {
  221. // generate request identifier and transaction ID
  222. RequestIdentifier reqID = parent.nextRequestID();
  223. int txID = parent.getMediaSession().getDriver().getNextTxID();
  224. RequestedAction[] actions = new RequestedAction[] { RequestedAction.NotifyImmediately };
  225. // constructs request
  226. NotificationRequest req = new NotificationRequest(this, parent.getEndpoint().getIdentifier(), reqID);
  227. ArrayList<EventName> signalList = new ArrayList<EventName>();
  228. ArrayList<RequestedEvent> eventList = new ArrayList();
  229. signalList.add(new EventName(PackageAU.Name, PackageAU.pr.withParm(params)));
  230. EventName[] signals = new EventName[signalList.size()];
  231. signalList.toArray(signals);
  232. //player events
  233. eventList.add(new RequestedEvent(new EventName(PackageAU.Name, MgcpEvent.oc), actions));
  234. eventList.add(new RequestedEvent(new EventName(PackageAU.Name, MgcpEvent.of), actions));
  235. RequestedEvent[] events = new RequestedEvent[eventList.size()];
  236. eventList.toArray(events);
  237. req.setRequestedEvents(events);
  238. req.setSignalRequests(signals);
  239. req.setTransactionHandle(txID);
  240. req.setNotifiedEntity(parent.getMediaSession().getDriver().getCallAgent());
  241. parent.getMediaSession().getDriver().attach(txID, this);
  242. parent.getMediaSession().getDriver().attach(reqID, this);
  243. parent.getMediaSession().getDriver().send(req);
  244. }
  245. private void stopRecording() {
  246. // generate request identifier and transaction ID
  247. RequestIdentifier reqID = parent.nextRequestID();
  248. int txID = parent.getMediaSession().getDriver().getNextTxID();
  249. // constructs request
  250. NotificationRequest req = new NotificationRequest(this, parent.getEndpoint().getIdentifier(), reqID);
  251. req.setTransactionHandle(txID);
  252. req.setNotifiedEntity(parent.getMediaSession().getDriver().getCallAgent());
  253. parent.getMediaSession().getDriver().attach(txID, this);
  254. parent.getMediaSession().getDriver().attach(reqID, this);
  255. parent.getMediaSession().getDriver().send(req);
  256. }
  257. private void signal(String signal) {
  258. try {
  259. fsm.signal(signal);
  260. } catch (UnknownTransitionException e) {
  261. }
  262. }
  263. private void fireEvent(RecorderEventImpl evt) {
  264. new Thread(new EventSender(evt)).start();
  265. }
  266. /**
  267. * Fires event.
  268. *
  269. * @param evt the event to be fired
  270. */
  271. private void fireEvent(EventName eventName) {
  272. switch (eventName.getEventIdentifier().intValue()) {
  273. case MgcpEvent.REPORT_ON_COMPLETION :
  274. //parse options
  275. Options options = new Options(eventName.getEventIdentifier().getParms());
  276. switch (options.getReturnCode()) {
  277. case 100 :
  278. if (options.getPatternIndex() >= 0) {
  279. recorderEvent = new RecorderEventImpl(this, RecorderEvent.RECORD_COMPLETED, true, RecorderEvent.RTC_TRIGGERED, triggers.get(options.getPatternIndex()), 0);
  280. } else if (options.getDigitsCollected() != null) {
  281. recorderEvent = new RecorderEventImpl(this, RecorderEvent.RECORD_COMPLETED, true, RecorderEvent.RTC_TRIGGERED, SignalDetector.DETECTION_OF_ONE_SIGNAL, 0);
  282. } else {
  283. recorderEvent = new RecorderEventImpl(this, RecorderEvent.RECORD_COMPLETED, true, RecorderEvent.NO_QUALIFIER, null, 0);
  284. }
  285. signal(SIGNAL_COMPLETE);
  286. break;
  287. case 328 :
  288. recorderEvent = new RecorderEventImpl(this, RecorderEvent.RECORD_COMPLETED, true, RecorderEvent.DURATION_EXCEEDED, null, 0);
  289. signal(SIGNAL_COMPLETE);
  290. break;
  291. case 327 :
  292. recorderEvent = new RecorderEventImpl(this, RecorderEvent.RECORD_COMPLETED, true, RecorderEvent.SILENCE, null, 0);
  293. signal(SIGNAL_COMPLETE);
  294. break;
  295. }
  296. break;
  297. case MgcpEvent.REPORT_FAILURE :
  298. recorderEvent = new RecorderEventImpl(this, RecorderEvent.RECORD_COMPLETED, true, MediaErr.NOT_FOUND, "Not found");
  299. signal(SIGNAL_FAILURE);
  300. break;
  301. }
  302. }
  303. public void processMgcpCommandEvent(JainMgcpCommandEvent event) {
  304. switch (event.getObjectIdentifier()) {
  305. case Constants.CMD_NOTIFY :
  306. Notify notify = (Notify) event;
  307. EventName[] events = notify.getObservedEvents();
  308. for (EventName evt: events) {
  309. fireEvent(evt);
  310. }
  311. break;
  312. default :
  313. return;
  314. }
  315. }
  316. public void processMgcpResponseEvent(JainMgcpResponseEvent event) {
  317. switch (event.getReturnCode().getValue()) {
  318. case ReturnCode.TRANSACTION_BEING_EXECUTED :
  319. break;
  320. case ReturnCode.TRANSACTION_EXECUTED_NORMALLY :
  321. try {
  322. fsm.signal(SIGNAL_SUCCESS);
  323. } catch (UnknownTransitionException e) {
  324. }
  325. break;
  326. default :
  327. try {
  328. fsm.signal(SIGNAL_FAILURE);
  329. } catch (UnknownTransitionException e) {
  330. }
  331. }
  332. }
  333. private class RecordRequest implements TransitionHandler {
  334. public void process(State state) {
  335. requestRecording();
  336. }
  337. }
  338. private class StopRequest implements StateEventHandler {
  339. public void onEvent(State state) {
  340. stopRecording();
  341. }
  342. }
  343. private class CompleteNotify implements TransitionHandler {
  344. public void process(State state) {
  345. fireEvent(recorderEvent);
  346. }
  347. }
  348. private class StoppedNotify implements TransitionHandler {
  349. public void process(State state) {
  350. fireEvent(new RecorderEventImpl(null, RecorderEvent.RECORD_COMPLETED, true, RecorderEvent.NO_QUALIFIER, null, 0));
  351. }
  352. }
  353. private class EventSender implements Runnable {
  354. protected RecorderEvent event;
  355. public EventSender(RecorderEvent event) {
  356. this.event = event;
  357. }
  358. public void run() {
  359. for (MediaEventListener l : listeners) {
  360. l.onEvent(event);
  361. }
  362. }
  363. }
  364. }