PageRenderTime 105ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/media/core/server-impl/src/main/java/org/mobicents/media/server/impl/AbstractSource.java

http://mobicents.googlecode.com/
Java | 520 lines | 262 code | 78 blank | 180 comment | 38 complexity | a9fcf79b117171f1597960797d64867a 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 XXXX, Red Hat Middleware LLC, and individual contributors as indicated
  4. * by the @authors tag. All rights reserved.
  5. * See the copyright.txt in the distribution for a full listing
  6. * of individual contributors.
  7. * This copyrighted material is made available to anyone wishing to use,
  8. * modify, copy, or redistribute it subject to the terms and conditions
  9. * of the GNU General Public License, v. 2.0.
  10. * This program is distributed in the hope that it will be useful, but WITHOUT A
  11. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
  12. * PARTICULAR PURPOSE. See the GNU General Public License for more details.
  13. * You should have received a copy of the GNU General Public License,
  14. * v. 2.0 along with this distribution; if not, write to the Free Software
  15. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
  16. * MA 02110-1301, USA.
  17. */
  18. package org.mobicents.media.server.impl;
  19. import java.util.Collection;
  20. import java.util.concurrent.locks.ReentrantLock;
  21. import org.apache.log4j.Logger;
  22. import org.mobicents.media.Buffer;
  23. import org.mobicents.media.Format;
  24. import org.mobicents.media.Inlet;
  25. import org.mobicents.media.MediaSink;
  26. import org.mobicents.media.MediaSource;
  27. import org.mobicents.media.Server;
  28. import org.mobicents.media.server.impl.clock.LocalTask;
  29. import org.mobicents.media.server.spi.events.NotifyEvent;
  30. /**
  31. * The base implementation of the Media source.
  32. *
  33. * <code>AbstractSource</code> and <code>AbstractSink</code> are implement general wirring contruct. All media
  34. * components have to extend one of these classes.
  35. *
  36. * @author Oleg Kulikov
  37. */
  38. public abstract class AbstractSource extends BaseComponent implements MediaSource {
  39. //reference to the media sink connected to this component.
  40. protected transient MediaSink otherParty;
  41. //state lock for locking state during start and stop
  42. private ReentrantLock state = new ReentrantLock();
  43. //transmission statisctics
  44. private long packetsTransmitted;
  45. private long bytesTransmitted;
  46. //shows if component is started or not.
  47. private volatile boolean started;
  48. //events generated by each component
  49. private NotifyEvent evtStarted;
  50. private NotifyEvent evtCompleted;
  51. private NotifyEvent evtStopped;
  52. //logger instance
  53. protected Logger logger;
  54. //local media time and packet sequence number.
  55. private volatile long timestamp = 0;
  56. private long sequenceNumber = 1;
  57. //duration is assigned
  58. private long duration = -1;
  59. private LocalTask worker;
  60. /**
  61. * Creates new instance of source with specified name.
  62. *
  63. * @param name
  64. * the name of the sink to be created.
  65. */
  66. public AbstractSource(String name) {
  67. super(name);
  68. logger = Logger.getLogger(getClass());
  69. evtStarted = new NotifyEventImpl(this, NotifyEvent.STARTED,"Started");
  70. evtCompleted = new NotifyEventImpl(this, NotifyEvent.COMPLETED,"Completed");
  71. evtStopped = new NotifyEventImpl(this, NotifyEvent.STOPPED,"Stoped");
  72. }
  73. /**
  74. * (Non Java-doc).
  75. *
  76. * @see org.mobicents.media.MediaSource#getMediaTime();
  77. */
  78. public long getMediaTime() {
  79. return timestamp;
  80. }
  81. /**
  82. * (Non Java-doc).
  83. *
  84. * @see org.mobicents.media.MediaSource#setMediaTime(long timestamp);
  85. */
  86. public void setMediaTime(long timestamp) {
  87. this.timestamp = timestamp;
  88. }
  89. /**
  90. * (Non Java-doc).
  91. *
  92. * @see org.mobicents.media.MediaSource#getDuration(long duration);
  93. */
  94. public long getDuration() {
  95. return duration;
  96. }
  97. /**
  98. * (Non Java-doc).
  99. *
  100. * @see org.mobicents.media.MediaSource#setDuration(long duration);
  101. */
  102. public void setDuration(long duration) {
  103. this.duration = duration;
  104. }
  105. /**
  106. * This method is called just before start.
  107. *
  108. * The descendant classes can verride this method and put additional logic
  109. */
  110. protected void beforeStart() throws Exception {
  111. }
  112. /**
  113. * (Non Java-doc).
  114. *
  115. * @see org.mobicents.media.MediaSource#start().
  116. */
  117. public void start() {
  118. state.lock();
  119. try {
  120. if (started) {
  121. return;
  122. }
  123. started = true;
  124. timestamp = 0;
  125. sequenceNumber = 0;
  126. beforeStart();
  127. if (otherParty != null && !otherParty.isStarted()) {
  128. otherParty.start();
  129. }
  130. //obtain current timestamp and schedule periodic execution
  131. //timestamp = syncSource.getTimestamp();
  132. worker = Server.scheduler.execute(this);
  133. //started!
  134. started();
  135. } catch (Exception e) {
  136. e.printStackTrace();
  137. started = false;
  138. failed(NotifyEvent.START_FAILED, e);
  139. } finally {
  140. state.unlock();
  141. }
  142. }
  143. /**
  144. * (Non Java-doc).
  145. *
  146. * @see org.mobicents.media.MediaSource#stop().
  147. */
  148. public void stop() {
  149. state.lock();
  150. try {
  151. started = false;
  152. if (worker != null) {
  153. worker.cancel();
  154. }
  155. if (logger.isDebugEnabled()) {
  156. logger.debug(this + " stopped");
  157. }
  158. if (otherParty != null && otherParty.isStarted()) {
  159. otherParty.stop();
  160. }
  161. stopped();
  162. afterStop();
  163. timestamp = 0;
  164. } finally {
  165. state.unlock();
  166. }
  167. }
  168. public void cancel() {
  169. stop();
  170. }
  171. public boolean isActive() {
  172. return started;
  173. }
  174. protected void setStarted(boolean started) {
  175. this.started = started;
  176. }
  177. /**
  178. * This method is called immediately after processing termination.
  179. *
  180. */
  181. public void afterStop() {
  182. }
  183. public boolean isMultipleConnectionsAllowed() {
  184. return false;
  185. }
  186. /**
  187. * This methods is called by media sink to setup preffered format.
  188. * Media source in opposite direction can ask media sink to get
  189. * preffered format by calling <code>sink.getPreffred(Collection<Format>)</code>
  190. * where collection is a subset of common formats.
  191. *
  192. * @param format preffred format.
  193. */
  194. public void setPreffered(Format format) {
  195. this.format = format;
  196. }
  197. /**
  198. * (Non Java-doc).
  199. *
  200. * @see org.mobicents.media.MediaSource#connect(MediaSink).
  201. */
  202. public void connect(MediaSink otherParty) {
  203. state.lock();
  204. try {
  205. // we can not connect with null
  206. if (otherParty == null) {
  207. throw new IllegalArgumentException("Other party can not be null");
  208. }
  209. //this implemntation suppose that other party is instance of AbstractSink
  210. if (!(otherParty instanceof AbstractSink)) {
  211. throw new IllegalArgumentException("Can not connect: " +
  212. otherParty + " does not extends AbstractSink");
  213. }
  214. //if other party allows multiple connection (like mixer or mux/demux
  215. //we should delegate connection procedure to other party because other party
  216. //maintances internal components
  217. if (otherParty.isMultipleConnectionsAllowed()) {
  218. otherParty.connect(this);
  219. return;
  220. }
  221. //if we are here then this is the most common case when we are joining
  222. //two components
  223. AbstractSink sink = (AbstractSink) otherParty;
  224. //calculating common formats
  225. Collection<Format> subset = this.subset(getFormats(), otherParty.getFormats());
  226. //connection is possible if and only if both components have common formats
  227. if (subset.isEmpty()) {
  228. throw new IllegalArgumentException("Format missmatch");
  229. }
  230. //now we have to ask sink to select preffered format
  231. //if sink can not determine preffred format at this time it will return null
  232. //and from now the sink is responsible for assigning preffred format to this sink
  233. Format preffered = sink.getPreffered(subset);
  234. if (preffered != null) {
  235. setPreffered(preffered);
  236. }
  237. //creating cross refernces to each other
  238. sink.otherParty = this;
  239. this.otherParty = sink;
  240. if (logger.isDebugEnabled()) {
  241. logger.debug(this + " is connected to " + otherParty);
  242. }
  243. } finally {
  244. state.unlock();
  245. }
  246. }
  247. /**
  248. * (Non Java-doc).
  249. *
  250. * @see org.mobicents.media.MediaSource#diconnection(MediaSink).
  251. */
  252. public void disconnect(MediaSink otherParty) {
  253. state.lock();
  254. try {
  255. // check argument
  256. if (otherParty == null) {
  257. throw new IllegalArgumentException("Other party can not be null");
  258. }
  259. //this implementation suppose to work with AbstractSink
  260. if (!(otherParty instanceof AbstractSink)) {
  261. throw new IllegalArgumentException("Can not disconnect: " + otherParty + " is not connected");
  262. }
  263. //if other party allows multiple connections then we have to deligate call to other party
  264. if (otherParty.isMultipleConnectionsAllowed()) {
  265. otherParty.disconnect(this);
  266. return;
  267. }
  268. //in this case we are checking that other party is connected to this component
  269. if (otherParty != this.otherParty) {
  270. throw new IllegalArgumentException("Can not disconnect: " + otherParty + " is not connected");
  271. }
  272. //indeed the other party is connected so we can break this connection
  273. //by removing cross reference and cleaning formats
  274. AbstractSink sink = (AbstractSink) otherParty;
  275. //cleaning formats
  276. setPreffered(null);
  277. sink.getPreffered(null);
  278. //cleaning references
  279. sink.otherParty = null;
  280. this.otherParty = null;
  281. } finally {
  282. state.unlock();
  283. }
  284. }
  285. public void connect(Inlet inlet) {
  286. connect(inlet.getInput());
  287. }
  288. public void disconnect(Inlet inlet) {
  289. disconnect(inlet.getInput());
  290. }
  291. /**
  292. * (Non Java-doc).
  293. *
  294. * @see org.mobicents.media.MediaSink#isConnected().
  295. */
  296. public boolean isConnected() {
  297. return otherParty != null;
  298. }
  299. /**
  300. * (Non Java-doc).
  301. *
  302. * @see org.mobicents.media.MediaSource#isStarted().
  303. */
  304. public boolean isStarted() {
  305. return this.started;
  306. }
  307. /**
  308. * This method must be overriden by concrete media source. The media have to fill buffer with media data and
  309. * attributes.
  310. *
  311. * @param buffer the buffer object for media.
  312. * @param sequenceNumber
  313. * the number of timer ticks from the begining.
  314. */
  315. public abstract void evolve(Buffer buffer, long timestamp);
  316. protected String getSupportedFormatList() {
  317. String s = "";
  318. if (otherParty != null) {
  319. Format[] formats = otherParty.getFormats();
  320. for (int i = 0; i < formats.length; i++) {
  321. s += formats[i] + ";";
  322. }
  323. }
  324. return s;
  325. }
  326. public int perform() {
  327. Buffer buffer = new Buffer();
  328. buffer.setFormat(format);
  329. evolve(buffer, timestamp);
  330. sequenceNumber = inc(sequenceNumber);
  331. buffer.setTimeStamp(timestamp);
  332. buffer.setSequenceNumber(sequenceNumber);
  333. //if buffer is marked as discarde we should not send it to consumer
  334. if (buffer.isDiscard()) {
  335. return (int) buffer.getDuration();
  336. }
  337. //timestamp is incremented if and only if buffer is not discarded
  338. timestamp += buffer.getDuration();
  339. if (logger.isTraceEnabled()) {
  340. logger.trace(this + " sending " + buffer + " to " + otherParty);
  341. }
  342. //let's do final check because other thread can disconect parties.
  343. if (otherParty == null) {
  344. return (int) buffer.getDuration();
  345. }
  346. //delivering data to the other party.
  347. try {
  348. if (buffer.getLength() > 0) {
  349. otherParty.receive(buffer);
  350. }
  351. } catch (Exception e) {
  352. logger.error("Can not deliver packet to " + otherParty, e);
  353. failed(NotifyEvent.TX_FAILED, e);
  354. }
  355. //at the final step we are incrementing stats
  356. packetsTransmitted++;
  357. bytesTransmitted += buffer.getLength();
  358. //if max duration is specified and media time reaches duration
  359. //the raise flag end of media.
  360. if (duration > 0 && timestamp >= duration) {
  361. buffer.setEOM(true);
  362. }
  363. //notify listeners and return negative duration
  364. if (buffer.isEOM()) {
  365. this.completed();
  366. return -1;
  367. }
  368. //return duration of this packet for scheduling next run
  369. return (int) buffer.getDuration();
  370. }
  371. /**
  372. * Sends notification that media processing has been started.
  373. */
  374. protected void started() {
  375. sendEvent(evtStarted);
  376. }
  377. /**
  378. * Sends failure notification.
  379. *
  380. * @param eventID
  381. * failure event identifier.
  382. * @param e
  383. * the exception caused failure.
  384. */
  385. protected void failed(int eventID, Exception e) {
  386. FailureEventImpl failed = new FailureEventImpl(this, eventID, e);
  387. worker.cancel();
  388. this.stop();
  389. sendEvent(failed);
  390. }
  391. /**
  392. * Sends notification that signal is completed.
  393. *
  394. */
  395. protected void completed() {
  396. worker.cancel();
  397. System.out.println("Stopping " + this);
  398. this.started = false;
  399. sendEvent(evtCompleted);
  400. }
  401. /**
  402. * Sends notification that detection is terminated.
  403. *
  404. */
  405. protected void stopped() {
  406. sendEvent(evtStopped);
  407. }
  408. /**
  409. * (Non Java-doc).
  410. *
  411. * @see org.mobicents.media.MediaSource#getPacketsReceived()
  412. */
  413. public long getPacketsTransmitted() {
  414. return packetsTransmitted;
  415. }
  416. /**
  417. * (Non Java-doc).
  418. *
  419. * @see org.mobicents.media.MediaSource#getBytesTransmitted()
  420. */
  421. public long getBytesTransmitted() {
  422. return bytesTransmitted;
  423. }
  424. @Override
  425. public void resetStats() {
  426. this.packetsTransmitted = 0;
  427. this.bytesTransmitted = 0;
  428. }
  429. private long inc(long v) {
  430. if (v == Long.MAX_VALUE) {
  431. v = -1;
  432. }
  433. return ++v;
  434. }
  435. /* (non-Javadoc)
  436. * @see org.mobicents.media.MediaSink#getInterface(java.lang.Class)
  437. */
  438. public <T> T getInterface(Class<T> interfaceType) {
  439. //should we check default?
  440. return null;
  441. }
  442. }