PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 1ms app.codeStats 0ms

/servers/media/chassis/src/main/java/org/mobicents/media/server/connection/Channel.java

http://mobicents.googlecode.com/
Java | 628 lines | 352 code | 107 blank | 169 comment | 16 complexity | 7c6516b320d21e57830b0411df130d05 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. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.mobicents.media.server.connection;
  23. import org.mobicents.media.CheckPoint;
  24. import org.mobicents.media.MediaSink;
  25. import org.mobicents.media.MediaSource;
  26. import org.mobicents.media.server.component.Mixer;
  27. import org.mobicents.media.server.component.Splitter;
  28. import org.mobicents.media.server.component.audio.AudioMixer;
  29. import org.mobicents.media.server.impl.PipeImpl;
  30. import org.mobicents.media.server.spi.ConnectionMode;
  31. import org.mobicents.media.server.spi.FormatNotSupportedException;
  32. import org.mobicents.media.server.spi.MediaType;
  33. import org.mobicents.media.server.spi.ModeNotSupportedException;
  34. import org.mobicents.media.server.impl.rtp.RTPDataChannel;
  35. import org.mobicents.media.server.spi.format.AudioFormat;
  36. import org.mobicents.media.server.spi.format.FormatFactory;
  37. import org.mobicents.media.server.spi.format.Formats;
  38. import org.mobicents.media.server.utils.Text;
  39. /**
  40. * Represents the bi-directional transition path for a particular media type stream.
  41. *
  42. *
  43. * @author kulikov
  44. */
  45. public class Channel {
  46. private final static AudioFormat DTMF = FormatFactory.createAudioFormat("telephone-event", 8000);
  47. static {
  48. DTMF.setOptions(new Text("0-15"));
  49. }
  50. private final static AudioFormat LINEAR = FormatFactory.createAudioFormat("linear", 8000, 16, 1);
  51. private final static Formats AUDIO_FORMATS = new Formats();
  52. static {
  53. AUDIO_FORMATS.add(LINEAR);
  54. }
  55. private BaseConnection connection;
  56. private MediaType mediaType;
  57. protected Mixer mixer;
  58. protected Splitter splitter;
  59. protected PipeImpl txPipe = new PipeImpl();
  60. protected PipeImpl rxPipe = new PipeImpl();
  61. private boolean isRTP = false;
  62. private RecvOnlyMode recvOnly;
  63. private SendOnlyMode sendOnly;
  64. private SendRecvMode send_recv;
  65. private NetworkLoopMode network_loop;
  66. private CnfMode cnfMode;
  67. private Mode mode;
  68. public Channel(BaseConnection connection, Connections connections, MediaType mediaType, Mixer mixer, Splitter splitter) throws Exception {
  69. this.connection = connection;
  70. this.mediaType = mediaType;
  71. //create mixer and assign intermediate format
  72. this.mixer = mixer;
  73. if (connections.dspFactory != null) {
  74. this.mixer.getOutput().setDsp(connections.dspFactory.newProcessor());
  75. }
  76. this.mixer.setFormat(connections.getAudioFormat());
  77. //this.mixer.getOutput().setFormats(AUDIO_FORMATS);
  78. //create splitter and assign intermediate format
  79. this.splitter = splitter;
  80. if (connections.dspFactory != null) {
  81. this.splitter.getInput().setDsp(connections.dspFactory.newProcessor());
  82. }
  83. this.splitter.setFormat(connections.getAudioFormat());
  84. splitter.getInput().setFormats(AUDIO_FORMATS);
  85. //create transmission switches
  86. recvOnly = new RecvOnlyMode(connections);
  87. sendOnly = new SendOnlyMode(connections);
  88. send_recv = new SendRecvMode(sendOnly, recvOnly);
  89. network_loop = new NetworkLoopMode(connections);
  90. cnfMode = new CnfMode(connection, connections, send_recv);
  91. }
  92. protected Formats getFormats() {
  93. Formats intersection = new Formats();
  94. Formats rx = splitter.getInput().getFormats();
  95. Formats tx = mixer.getOutput().getFormats();
  96. rx.intersection(tx, intersection);
  97. intersection.add(DTMF);
  98. return intersection;
  99. }
  100. protected void selectFormats(Formats fmts) {
  101. try {
  102. mixer.getOutput().setFormats(fmts);
  103. } catch (FormatNotSupportedException e) {
  104. //never happen
  105. }
  106. }
  107. /**
  108. * Modify gain of the tx stream.
  109. *
  110. * Applicable for audio channel only.
  111. *
  112. * @param gain the value of the gain.
  113. */
  114. public void setGain(double gain) {
  115. ((AudioMixer) mixer).setGain(gain);
  116. }
  117. /**
  118. * Gets the media type of this channel.
  119. *
  120. * @return the media type identifier.
  121. */
  122. public MediaType getMediaType() {
  123. return mediaType;
  124. }
  125. /**
  126. * Changes transmission mode.
  127. *
  128. * @param mode the identifier of transmission mode.
  129. * @throws ModeNotSupportedException
  130. */
  131. public void setMode(ConnectionMode mode) throws ModeNotSupportedException {
  132. if (this.mode != null) {
  133. this.mode.deactivate();
  134. }
  135. this.mode = convert(mode);
  136. if (this.mode != null) {
  137. try {
  138. this.mode.activate();
  139. } catch (FormatNotSupportedException e) {
  140. throw new ModeNotSupportedException(e.getMessage());
  141. }
  142. }
  143. }
  144. /**
  145. * Gets the current transmission mode.
  146. *
  147. * @return the mode identifier.
  148. */
  149. public ConnectionMode getMode() {
  150. return mode == null ? ConnectionMode.INACTIVE : this.mode.getID();
  151. }
  152. /**
  153. * Creates switch executor for specified mode.
  154. *
  155. * @param mode the mode value.
  156. * @return switch executor
  157. * @throws ModeNotSupportedException
  158. */
  159. private Mode convert(ConnectionMode mode) throws ModeNotSupportedException {
  160. switch (mode) {
  161. case RECV_ONLY:
  162. return recvOnly;
  163. case SEND_ONLY:
  164. return sendOnly;
  165. case SEND_RECV:
  166. return send_recv;
  167. case CONFERENCE:
  168. return cnfMode;
  169. case NETWORK_LOOPBACK:
  170. return network_loop;
  171. case INACTIVE:
  172. return null;
  173. default:
  174. throw new ModeNotSupportedException(mode);
  175. }
  176. }
  177. /**
  178. * Gets the access to the specified check point.
  179. *
  180. * @param i the check point identifier.
  181. * @return check point.
  182. */
  183. public CheckPoint getCheckPoint(int i) {
  184. switch(i) {
  185. case 5:
  186. return sendOnly.getCheckPoint(5);
  187. case 6:
  188. return recvOnly.getCheckPoint(6);
  189. case 7:
  190. return sendOnly.getCheckPoint(7);
  191. case 8:
  192. return recvOnly.getCheckPoint(8);
  193. case 9:
  194. return new CheckPointImpl(mixer.getOutput().getPacketsTransmitted(), mixer.getOutput().getBytesTransmitted());
  195. case 10:
  196. return new CheckPointImpl(splitter.getInput().getPacketsReceived(), splitter.getInput().getBytesReceived());
  197. default: throw new IllegalArgumentException();
  198. }
  199. }
  200. public String report() {
  201. StringBuilder builder = new StringBuilder();
  202. builder.append(splitter.report());
  203. builder.append(mixer.report());
  204. return builder.toString();
  205. }
  206. public void connect(RTPDataChannel other) {
  207. this.isRTP = true;
  208. txPipe.connect(mixer.getOutput());
  209. txPipe.connect(other.getOutput());
  210. rxPipe.connect(splitter.getInput());
  211. rxPipe.connect(other.getInput());
  212. }
  213. /**
  214. * Joins with other channel
  215. *
  216. * @param other the other channel
  217. */
  218. public void connect(Channel other) {
  219. txPipe.connect(mixer.getOutput());
  220. txPipe.connect(other.splitter.getInput());
  221. other.txPipe.connect(other.mixer.getOutput());
  222. other.txPipe.connect(splitter.getInput());
  223. txPipe.start();
  224. other.txPipe.start();
  225. }
  226. /**
  227. * Disconnects this channel from another if it was connected previously
  228. */
  229. public void disconnect() {
  230. txPipe.stop();
  231. txPipe.disconnect();
  232. }
  233. /**
  234. * Transmission mode
  235. */
  236. private abstract class Mode {
  237. /**
  238. * Gets the unique identifier of the mode.
  239. *
  240. * @return mode identifier.
  241. */
  242. public abstract ConnectionMode getID();
  243. /**
  244. * Establishes transmission path according to this mode.
  245. */
  246. public abstract void activate() throws FormatNotSupportedException;
  247. /**
  248. * Clears media path.
  249. */
  250. public abstract void deactivate();
  251. }
  252. /**
  253. * Transmission from channel to connections
  254. */
  255. private class RecvOnlyMode extends Mode {
  256. private Connections connections;
  257. //connection's splitter output
  258. private MediaSource source;
  259. //endpoint's mixer input
  260. private MediaSink sink;
  261. //pipe for joining
  262. private PipeImpl pipe = new PipeImpl();
  263. public RecvOnlyMode(Connections connections) {
  264. this.connections = connections;
  265. }
  266. @Override
  267. public void activate() throws FormatNotSupportedException {
  268. //create input/output
  269. source = splitter.newOutput();
  270. sink = connections.getMixer(mediaType).newInput();
  271. //assign formats
  272. sink.setFormats(connections.getFormats(mediaType));
  273. source.setFormats(connections.getFormats(mediaType));
  274. //join
  275. pipe.connect(source);
  276. pipe.connect(sink);
  277. //enable transmission
  278. pipe.start();
  279. rxPipe.start();
  280. }
  281. @Override
  282. public void deactivate() {
  283. //break transmission
  284. rxPipe.stop();
  285. pipe.stop();
  286. //drop connection
  287. pipe.disconnect();
  288. //release input/output
  289. splitter.release(source);
  290. connections.getMixer(mediaType).release(sink);
  291. }
  292. @Override
  293. public ConnectionMode getID() {
  294. return ConnectionMode.RECV_ONLY;
  295. }
  296. /**
  297. * Get access to check points.
  298. *
  299. * @param i the identifier of check point.
  300. * @return check point.
  301. */
  302. public CheckPoint getCheckPoint(int i) {
  303. switch(i) {
  304. case 8:
  305. return new CheckPointImpl(source.getPacketsTransmitted(), source.getBytesTransmitted());
  306. case 6:
  307. return new CheckPointImpl(sink.getPacketsReceived(), sink.getBytesReceived());
  308. default:
  309. throw new IllegalArgumentException();
  310. }
  311. }
  312. }
  313. /**
  314. * Transmission from connections to channel.
  315. */
  316. public class SendOnlyMode extends Mode {
  317. private Connections connections;
  318. //connection's splitter output
  319. private MediaSource source;
  320. //endpoint's mixer input
  321. private MediaSink sink;
  322. //pipe for joining
  323. private PipeImpl pipe = new PipeImpl();
  324. private long mediaTime;
  325. public SendOnlyMode(Connections connections) {
  326. this.connections = connections;
  327. }
  328. @Override
  329. public void activate() throws FormatNotSupportedException {
  330. //create input/output
  331. source = connections.getSplitter(mediaType).newOutput();
  332. sink = mixer.newInput();
  333. //assign formats
  334. sink.setFormats(connections.getFormats(mediaType));
  335. source.setFormats(connections.getFormats(mediaType));
  336. //join
  337. pipe.connect(source);
  338. pipe.connect(sink);
  339. //start transmission
  340. mixer.getOutput().setMediaTime(mediaTime);
  341. mixer.start();
  342. pipe.start();
  343. txPipe.start();
  344. }
  345. @Override
  346. public void deactivate() {
  347. //remember media time
  348. this.mediaTime = mixer.getOutput().getMediaTime();
  349. //break transmission
  350. txPipe.stop();
  351. mixer.stop();
  352. pipe.stop();
  353. //unjoin
  354. pipe.disconnect();
  355. //release input/output
  356. connections.getSplitter(mediaType).release(source);
  357. mixer.release(sink);
  358. }
  359. @Override
  360. public ConnectionMode getID() {
  361. return ConnectionMode.SEND_ONLY;
  362. }
  363. /**
  364. * Get access to check points.
  365. *
  366. * @param i the identifier of check point.
  367. * @return check point.
  368. */
  369. public CheckPoint getCheckPoint(int i) {
  370. switch(i) {
  371. case 5:
  372. return new CheckPointImpl(source != null ? source.getPacketsTransmitted() :0, source != null ? source.getBytesTransmitted() : 0);
  373. case 7:
  374. return new CheckPointImpl(sink != null ? sink.getPacketsReceived() : 0, sink != null ? sink.getBytesReceived() : 0);
  375. default:
  376. throw new IllegalArgumentException();
  377. }
  378. }
  379. }
  380. /**
  381. * Transmission in both directions
  382. */
  383. public class SendRecvMode extends Mode {
  384. //sendrecv mode is a combination of send and recv only
  385. private SendOnlyMode sendOnly;
  386. private RecvOnlyMode recvOnly;
  387. private SendRecvMode(SendOnlyMode sendOnly, RecvOnlyMode recvOnly) {
  388. this.sendOnly = sendOnly;
  389. this.recvOnly = recvOnly;
  390. }
  391. @Override
  392. public void activate() throws FormatNotSupportedException {
  393. recvOnly.activate();
  394. sendOnly.activate();
  395. }
  396. @Override
  397. public void deactivate() {
  398. sendOnly.deactivate();
  399. recvOnly.deactivate();
  400. }
  401. @Override
  402. public ConnectionMode getID() {
  403. return ConnectionMode.SEND_RECV;
  404. }
  405. }
  406. /**
  407. * Terminates channel to itself
  408. */
  409. public class NetworkLoopMode extends Mode {
  410. private Connections connections;
  411. //connection's splitter output
  412. private MediaSource tx;
  413. //endpoint's mixer input
  414. private MediaSink rx;
  415. //pipe for joining
  416. private PipeImpl pipe = new PipeImpl();
  417. public NetworkLoopMode(Connections connections) {
  418. this.connections = connections;
  419. }
  420. @Override
  421. public void activate() throws FormatNotSupportedException {
  422. //create input/output
  423. tx = splitter.newOutput();
  424. rx = mixer.newInput();
  425. tx.setFormats(connections.getFormats(mediaType));
  426. rx.setFormats(connections.getFormats(mediaType));
  427. //join
  428. pipe.connect(tx);
  429. pipe.connect(rx);
  430. //start transmission
  431. //pipe.setDebug(true);
  432. pipe.start();
  433. //start RTP
  434. if (isRTP) {
  435. rxPipe.start();
  436. txPipe.start();
  437. }
  438. }
  439. @Override
  440. public void deactivate() {
  441. //start RTP
  442. if (isRTP) {
  443. rxPipe.stop();
  444. txPipe.stop();
  445. }
  446. pipe.stop();
  447. //unjoin
  448. pipe.disconnect();
  449. //release input/output
  450. splitter.release(tx);
  451. mixer.release(rx);
  452. }
  453. @Override
  454. public ConnectionMode getID() {
  455. return ConnectionMode.NETWORK_LOOPBACK;
  456. }
  457. }
  458. /**
  459. * Conference mode
  460. */
  461. private class CnfMode extends Mode {
  462. private Connections connections;
  463. private BaseConnection connection;
  464. private SendRecvMode sendRecv;
  465. public CnfMode(BaseConnection connection, Connections connections, SendRecvMode sendRecv) {
  466. this.connection = connection;
  467. this.connections = connections;
  468. this.sendRecv = sendRecv;
  469. }
  470. @Override
  471. public ConnectionMode getID() {
  472. return ConnectionMode.CONFERENCE;
  473. }
  474. @Override
  475. public void activate() throws FormatNotSupportedException {
  476. connections.addToConference(connection);
  477. sendRecv.activate();
  478. }
  479. @Override
  480. public void deactivate() {
  481. connections.removeFromConference(connection);
  482. sendRecv.deactivate();
  483. }
  484. }
  485. /**
  486. * Implements connection's check points
  487. */
  488. private class CheckPointImpl implements CheckPoint {
  489. private int frames, bytes;
  490. /**
  491. * Creates check point instance.
  492. *
  493. * @param frames
  494. * @param bytes
  495. */
  496. protected CheckPointImpl(long frames, long bytes) {
  497. this.frames = (int) frames;
  498. this.bytes = (int) bytes;
  499. }
  500. /**
  501. * (Non Java-doc.)
  502. *
  503. * @see org.mobicents.media.CheckPoint#getFrames()
  504. */
  505. public int getFrames() {
  506. return this.frames;
  507. }
  508. /**
  509. * (Non Java-doc.)
  510. *
  511. * @see org.mobicents.media.CheckPoint#getBytes()
  512. */
  513. public int getBytes() {
  514. return this.bytes;
  515. }
  516. @Override
  517. public String toString() {
  518. return String.format("frame=%d, bytes=%d", frames, bytes);
  519. }
  520. }
  521. }